Compare commits

..

No commits in common. "9127f97cc9f5aa0d79def9270b6251b047e22054" and "71c25c778f69a1b3a3f70f28d895150c93bd0aa4" have entirely different histories.

11 changed files with 38 additions and 99 deletions

View File

@ -1,10 +1,10 @@
<?php <?php
use App\App;
use App\Core\NullLogger; use App\Core\NullLogger;
class RateLimiter { class RateLimiter {
public $db; public $db;
private $database;
/** @var mixed NullLogger (or PSR-3 logger) or plugin Log */ /** @var mixed NullLogger (or PSR-3 logger) or plugin Log */
private $logger; private $logger;
public $maxAttempts = 5; // Maximum login attempts public $maxAttempts = 5; // Maximum login attempts
@ -27,13 +27,12 @@ class RateLimiter {
]; ];
/** /**
* @param mixed $database Database object
* @param mixed $logger Optional NullLogger (or PSR-3 logger) or plugin Log * @param mixed $logger Optional NullLogger (or PSR-3 logger) or plugin Log
*/ */
public function __construct($logger = null) { public function __construct($database, $logger = null) {
$db = App::db(); $this->database = $database;
// Extract PDO connection from Database object $this->db = $database->getConnection();
$this->db = ($db instanceof PDO) ? $db : $db->getConnection();
// Initialize logger (plugin Log if present or NullLogger otherwise) // Initialize logger (plugin Log if present or NullLogger otherwise)
if ($logger !== null) { if ($logger !== null) {
$this->logger = $logger; $this->logger = $logger;

View File

@ -1,7 +1,5 @@
<?php <?php
use App\App;
/** /**
* class User * class User
* *
@ -35,7 +33,7 @@ class User {
require_once __DIR__ . '/ratelimiter.php'; require_once __DIR__ . '/ratelimiter.php';
require_once __DIR__ . '/twoFactorAuth.php'; require_once __DIR__ . '/twoFactorAuth.php';
$this->rateLimiter = new RateLimiter(); $this->rateLimiter = new RateLimiter($database);
$this->twoFactorAuth = new TwoFactorAuthentication($database); $this->twoFactorAuth = new TwoFactorAuthentication($database);
} }

View File

@ -2,8 +2,6 @@
namespace App\Core; namespace App\Core;
use App\App;
class Maintenance class Maintenance
{ {
// Keep it simple: store the flag within the app directory // Keep it simple: store the flag within the app directory
@ -15,11 +13,10 @@ class Maintenance
return true; return true;
} }
// Prefer DB settings if available in the current request // Prefer DB settings if available in the current request
$db = App::db(); if (isset($GLOBALS['db'])) {
if ($db) {
try { try {
require_once __DIR__ . '/Settings.php'; require_once __DIR__ . '/Settings.php';
$settings = new Settings($db); $settings = new Settings($GLOBALS['db']);
return $settings->get('maintenance_enabled', '0') === '1'; return $settings->get('maintenance_enabled', '0') === '1';
} catch (\Throwable $e) { } catch (\Throwable $e) {
// fall back to file flag // fall back to file flag
@ -30,11 +27,10 @@ class Maintenance
public static function enable(string $message = ''): bool public static function enable(string $message = ''): bool
{ {
$db = App::db(); if (isset($GLOBALS['db'])) {
if ($db) {
try { try {
require_once __DIR__ . '/Settings.php'; require_once __DIR__ . '/Settings.php';
$settings = new Settings($db); $settings = new Settings($GLOBALS['db']);
$ok1 = $settings->set('maintenance_enabled', '1'); $ok1 = $settings->set('maintenance_enabled', '1');
$ok2 = $settings->set('maintenance_message', $message); $ok2 = $settings->set('maintenance_message', $message);
return $ok1 && $ok2; return $ok1 && $ok2;
@ -52,11 +48,10 @@ class Maintenance
public static function disable(): bool public static function disable(): bool
{ {
$db = App::db(); if (isset($GLOBALS['db'])) {
if ($db) {
try { try {
require_once __DIR__ . '/Settings.php'; require_once __DIR__ . '/Settings.php';
$settings = new Settings($db); $settings = new Settings($GLOBALS['db']);
$ok1 = $settings->set('maintenance_enabled', '0'); $ok1 = $settings->set('maintenance_enabled', '0');
// keep last message for reference, optional to clear // keep last message for reference, optional to clear
return $ok1; return $ok1;
@ -79,11 +74,10 @@ class Maintenance
if ($envMsg) { if ($envMsg) {
return trim($envMsg); return trim($envMsg);
} }
$db = App::db(); if (isset($GLOBALS['db'])) {
if ($db) {
try { try {
require_once __DIR__ . '/Settings.php'; require_once __DIR__ . '/Settings.php';
$settings = new Settings($db); $settings = new Settings($GLOBALS['db']);
return (string)$settings->get('maintenance_message', ''); return (string)$settings->get('maintenance_message', '');
} catch (\Throwable $e) { } catch (\Throwable $e) {
// ignore and fall back to file flag // ignore and fall back to file flag

View File

@ -167,9 +167,9 @@ class PluginManager
return false; return false;
} }
// Use App API to get database connection // Use global DB and get PDO connection
$db = \App\App::db(); $db = $GLOBALS['db'];
$pdo = ($db instanceof \PDO) ? $db : $db->getConnection(); $pdo = $db->getConnection();
try { try {
// Update or insert plugin setting in database // Update or insert plugin setting in database
@ -213,15 +213,9 @@ class PluginManager
return false; return false;
} }
// Use App API to get database connection // Use global DB and get PDO connection
$db = \App\App::db(); $db = $GLOBALS['db'];
$pdo = $db->getConnection();
// If database unavailable, fallback to manifest
if (!$db) {
return self::$catalog[$plugin]['meta']['enabled'] ?? false;
}
$pdo = ($db instanceof \PDO) ? $db : $db->getConnection();
try { try {
$stmt = $pdo->prepare('SELECT `value` FROM settings WHERE `key` = :key LIMIT 1'); $stmt = $pdo->prepare('SELECT `value` FROM settings WHERE `key` = :key LIMIT 1');
@ -232,8 +226,7 @@ class PluginManager
return $result && $result['value'] === '1'; return $result && $result['value'] === '1';
} catch (\PDOException $e) { } catch (\PDOException $e) {
app_log('error', 'PluginManager::isEnabled failed for ' . $plugin . ': ' . $e->getMessage(), ['scope' => 'plugin']); app_log('error', 'PluginManager::isEnabled failed for ' . $plugin . ': ' . $e->getMessage(), ['scope' => 'plugin']);
// Fallback to manifest on database error return false;
return self::$catalog[$plugin]['meta']['enabled'] ?? false;
} }
} }
@ -283,28 +276,24 @@ class PluginManager
return false; return false;
} }
$db = \App\App::db(); global $db;
if (!$db) { if (!$db instanceof PDO) {
app_log('error', 'PluginManager::purge: Database connection not available', ['scope' => 'plugin']); app_log('error', 'PluginManager::purge: Database connection not available', ['scope' => 'plugin']);
return false; return false;
} }
$pdo = ($db instanceof \PDO) ? $db : $db->getConnection();
try { try {
// First disable the plugin // First disable the plugin
self::setEnabled($plugin, false); self::setEnabled($plugin, false);
// Remove plugin settings // Remove plugin settings
$stmt = $pdo->prepare('DELETE FROM settings WHERE `key` LIKE :pattern'); $stmt = $db->prepare('DELETE FROM settings WHERE `key` LIKE :pattern');
$stmt->execute([':pattern' => 'plugin_enabled_' . $plugin]); $stmt->execute([':pattern' => 'plugin_enabled_' . $plugin]);
// Drop plugin-specific tables (user_pro_* tables for this plugin) // Drop plugin-specific tables (user_pro_* tables for this plugin)
$stmt = $pdo->prepare('SHOW TABLES LIKE "user_pro_%"'); $stmt = $db->prepare('SHOW TABLES LIKE "user_pro_%"');
$stmt->execute(); $stmt->execute();
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
// Disable foreign key checks temporarily to allow table drops
$pdo->exec('SET FOREIGN_KEY_CHECKS=0');
foreach ($tables as $table) { foreach ($tables as $table) {
// Check if this table belongs to the plugin by checking its migration file // Check if this table belongs to the plugin by checking its migration file
@ -312,15 +301,12 @@ class PluginManager
if (file_exists($migrationFile)) { if (file_exists($migrationFile)) {
$migrationContent = file_get_contents($migrationFile); $migrationContent = file_get_contents($migrationFile);
if (strpos($migrationContent, $table) !== false) { if (strpos($migrationContent, $table) !== false) {
$pdo->exec("DROP TABLE IF EXISTS `$table`"); $db->exec("DROP TABLE IF EXISTS `$table`");
app_log('info', 'PluginManager::purge: Dropped table ' . $table . ' for plugin ' . $plugin, ['scope' => 'plugin']); app_log('info', 'PluginManager::purge: Dropped table ' . $table . ' for plugin ' . $plugin, ['scope' => 'plugin']);
} }
} }
} }
// Re-enable foreign key checks
$pdo->exec('SET FOREIGN_KEY_CHECKS=1');
app_log('info', 'PluginManager::purge: Successfully purged plugin ' . $plugin, ['scope' => 'plugin']); app_log('info', 'PluginManager::purge: Successfully purged plugin ' . $plugin, ['scope' => 'plugin']);
return true; return true;
} catch (Throwable $e) { } catch (Throwable $e) {

View File

@ -23,8 +23,7 @@ try {
// Initialize RateLimiter // Initialize RateLimiter
require_once '../app/classes/ratelimiter.php'; require_once '../app/classes/ratelimiter.php';
$rateLimiter = new RateLimiter(); $rateLimiter = new RateLimiter($db);
// Get user IP // Get user IP
require_once '../app/helpers/ip_helper.php'; require_once '../app/helpers/ip_helper.php';
$user_IP = getUserIP(); $user_IP = getUserIP();

View File

@ -9,7 +9,7 @@ use App\App;
*/ */
class Register { class Register {
/** /**
* @var PDO $db The database connection instance. * @var PDO|null $db The database connection instance.
*/ */
private $db; private $db;
private $rateLimiter; private $rateLimiter;
@ -18,14 +18,16 @@ class Register {
/** /**
* Register constructor. * Register constructor.
* Initializes the database connection using App API. * Initializes the database connection using App API.
*
* @param PDO|null $database The database connection (optional, will use App::db() if not provided).
*/ */
public function __construct() { public function __construct($database = null) {
$this->db = App::db(); $this->db = $database instanceof PDO ? $database : App::db();
require_once APP_PATH . 'classes/ratelimiter.php'; require_once APP_PATH . 'classes/ratelimiter.php';
require_once APP_PATH . 'classes/twoFactorAuth.php'; require_once APP_PATH . 'classes/twoFactorAuth.php';
$this->rateLimiter = new RateLimiter(); $this->rateLimiter = new RateLimiter($this->db);
$this->twoFactorAuth = new TwoFactorAuthentication($this->db); $this->twoFactorAuth = new TwoFactorAuthentication($this->db);
} }

View File

@ -1,13 +1,11 @@
<?php <?php
require_once dirname(__DIR__, 3) . '/app/core/App.php';
require_once dirname(__DIR__, 3) . '/app/classes/database.php'; require_once dirname(__DIR__, 3) . '/app/classes/database.php';
require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php'; require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php';
require_once dirname(__DIR__, 3) . '/app/classes/log.php'; require_once dirname(__DIR__, 3) . '/app/classes/log.php';
require_once dirname(__DIR__, 3) . '/app/includes/rate_limit_middleware.php'; require_once dirname(__DIR__, 3) . '/app/includes/rate_limit_middleware.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use App\App;
class RateLimitMiddlewareTest extends TestCase class RateLimitMiddlewareTest extends TestCase
{ {
@ -36,11 +34,8 @@ class RateLimitMiddlewareTest extends TestCase
'password' => $password 'password' => $password
]); ]);
// Set up App::db() for RateLimiter
App::set('db', $this->db->getConnection());
// Create rate limiter instance // Create rate limiter instance
$this->rateLimiter = new RateLimiter(); $this->rateLimiter = new RateLimiter($this->db);
// Drop tables if they exist // Drop tables if they exist
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth");
@ -124,10 +119,6 @@ class RateLimitMiddlewareTest extends TestCase
$this->db->getConnection()->exec("TRUNCATE TABLE security_ip_whitelist"); $this->db->getConnection()->exec("TRUNCATE TABLE security_ip_whitelist");
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_auth"); $this->db->getConnection()->exec("TRUNCATE TABLE security_rate_auth");
$this->db->getConnection()->exec("TRUNCATE TABLE log"); $this->db->getConnection()->exec("TRUNCATE TABLE log");
// Clean up App state
App::reset('db');
parent::tearDown(); parent::tearDown();
} }

View File

@ -1,12 +1,10 @@
<?php <?php
require_once dirname(__DIR__, 3) . '/app/core/App.php';
require_once dirname(__DIR__, 3) . '/app/classes/database.php'; require_once dirname(__DIR__, 3) . '/app/classes/database.php';
require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php'; require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php';
require_once dirname(__DIR__, 3) . '/app/classes/log.php'; require_once dirname(__DIR__, 3) . '/app/classes/log.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use App\App;
class RateLimiterTest extends TestCase class RateLimiterTest extends TestCase
{ {
@ -31,11 +29,8 @@ class RateLimiterTest extends TestCase
'password' => $password 'password' => $password
]); ]);
// Set up App::db() for RateLimiter
App::set('db', $this->db->getConnection());
// The RateLimiter constructor will create all necessary tables // The RateLimiter constructor will create all necessary tables
$this->rateLimiter = new RateLimiter(); $this->rateLimiter = new RateLimiter($this->db);
} }
protected function tearDown(): void protected function tearDown(): void
@ -45,10 +40,6 @@ class RateLimiterTest extends TestCase
$this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->pagesRatelimitTable}"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->pagesRatelimitTable}");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->blacklistTable}"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->blacklistTable}");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->whitelistTable}"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->whitelistTable}");
// Clean up App state
App::reset('db');
parent::tearDown(); parent::tearDown();
} }

View File

@ -1,13 +1,11 @@
<?php <?php
require_once dirname(__DIR__, 3) . '/app/core/App.php';
require_once dirname(__DIR__, 3) . '/app/classes/database.php'; require_once dirname(__DIR__, 3) . '/app/classes/database.php';
require_once dirname(__DIR__, 3) . '/app/classes/user.php'; require_once dirname(__DIR__, 3) . '/app/classes/user.php';
require_once dirname(__DIR__, 3) . '/plugins/register/models/register.php'; require_once dirname(__DIR__, 3) . '/plugins/register/models/register.php';
require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php'; require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use App\App;
class UserRegisterTest extends TestCase class UserRegisterTest extends TestCase
{ {
@ -32,9 +30,6 @@ class UserRegisterTest extends TestCase
'password' => $password 'password' => $password
]); ]);
// Set up App::db() for Register class to use
App::set('db', $this->db->getConnection());
// Create user table with MariaDB syntax // Create user table with MariaDB syntax
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user ( CREATE TABLE IF NOT EXISTS user (
@ -83,15 +78,12 @@ class UserRegisterTest extends TestCase
) )
"); ");
$this->register = new Register(); $this->register = new Register($this->db);
$this->user = new User($this->db); $this->user = new User($this->db);
} }
protected function tearDown(): void protected function tearDown(): void
{ {
// Clean up App state
App::reset('db');
// Drop tables in correct order // Drop tables in correct order
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user_2fa"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS user_2fa");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth");

View File

@ -1,13 +1,11 @@
<?php <?php
require_once dirname(__DIR__, 3) . '/app/core/App.php';
require_once dirname(__DIR__, 3) . '/app/classes/database.php'; require_once dirname(__DIR__, 3) . '/app/classes/database.php';
require_once dirname(__DIR__, 3) . '/app/classes/user.php'; require_once dirname(__DIR__, 3) . '/app/classes/user.php';
require_once dirname(__DIR__, 3) . '/plugins/register/models/register.php'; require_once dirname(__DIR__, 3) . '/plugins/register/models/register.php';
require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php'; require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use App\App;
class UserTest extends TestCase class UserTest extends TestCase
{ {
@ -32,9 +30,6 @@ class UserTest extends TestCase
'password' => $password 'password' => $password
]); ]);
// Set up App::db() for Register class to use
App::set('db', $this->db->getConnection());
// Create user table with MariaDB syntax // Create user table with MariaDB syntax
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user ( CREATE TABLE IF NOT EXISTS user (
@ -84,14 +79,11 @@ class UserTest extends TestCase
"); ");
$this->user = new User($this->db); $this->user = new User($this->db);
$this->register = new Register(); $this->register = new Register($this->db);
} }
protected function tearDown(): void protected function tearDown(): void
{ {
// Clean up App state
App::reset('db');
// Drop tables in correct order // Drop tables in correct order
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user_2fa"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS user_2fa");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth"); $this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth");

View File

@ -12,11 +12,6 @@ if (!headers_sent()) {
ini_set('session.gc_maxlifetime', 1440); // 24 minutes ini_set('session.gc_maxlifetime', 1440); // 24 minutes
} }
// Define APP_PATH for components that expect the constant
if (!defined('APP_PATH')) {
define('APP_PATH', dirname(__DIR__) . '/app/');
}
// load the main App registry and plugin route registry // load the main App registry and plugin route registry
require_once __DIR__ . '/../app/core/App.php'; require_once __DIR__ . '/../app/core/App.php';
require_once __DIR__ . '/../app/core/PluginRouteRegistry.php'; require_once __DIR__ . '/../app/core/PluginRouteRegistry.php';