Compare commits

..

No commits in common. "4a43d8cfc79563a1e5279ea5a0d42d596f359d6e" and "630f71ce4dbdf1d3fbfca635116d7651ad21dbb6" have entirely different histories.

37 changed files with 729 additions and 998 deletions

View File

@ -43,11 +43,11 @@ class Agent {
jat.endpoint AS agent_endpoint, jat.endpoint AS agent_endpoint,
h.platform_id h.platform_id
FROM FROM
jilo_agent ja jilo_agents ja
JOIN JOIN
jilo_agent_type jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN JOIN
host h ON ja.host_id = h.id hosts h ON ja.host_id = h.id
WHERE WHERE
ja.host_id = :host_id'; ja.host_id = :host_id';
@ -87,11 +87,11 @@ class Agent {
jat.endpoint AS agent_endpoint, jat.endpoint AS agent_endpoint,
h.platform_id h.platform_id
FROM FROM
jilo_agent ja jilo_agents ja
JOIN JOIN
jilo_agent_type jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN JOIN
host h ON ja.host_id = h.id hosts h ON ja.host_id = h.id
WHERE WHERE
ja.id = :agent_id'; ja.id = :agent_id';
@ -110,7 +110,7 @@ class Agent {
*/ */
public function getAgentTypes() { public function getAgentTypes() {
$sql = 'SELECT * $sql = 'SELECT *
FROM jilo_agent_type FROM jilo_agent_types
ORDER BY id'; ORDER BY id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute(); $query->execute();
@ -131,7 +131,7 @@ class Agent {
id, id,
agent_type_id agent_type_id
FROM FROM
jilo_agent jilo_agents
WHERE WHERE
host_id = :host_id'; host_id = :host_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
@ -152,7 +152,7 @@ class Agent {
*/ */
public function addAgent($host_id, $newAgent) { public function addAgent($host_id, $newAgent) {
try { try {
$sql = 'INSERT INTO jilo_agent $sql = 'INSERT INTO jilo_agents
(host_id, agent_type_id, url, secret_key, check_period) (host_id, agent_type_id, url, secret_key, check_period)
VALUES VALUES
(:host_id, :agent_type_id, :url, :secret_key, :check_period)'; (:host_id, :agent_type_id, :url, :secret_key, :check_period)';
@ -184,7 +184,7 @@ class Agent {
*/ */
public function editAgent($agent_id, $updatedAgent) { public function editAgent($agent_id, $updatedAgent) {
try { try {
$sql = 'UPDATE jilo_agent $sql = 'UPDATE jilo_agents
SET SET
agent_type_id = :agent_type_id, agent_type_id = :agent_type_id,
url = :url, url = :url,
@ -222,7 +222,7 @@ class Agent {
*/ */
public function deleteAgent($agent_id) { public function deleteAgent($agent_id) {
try { try {
$sql = 'DELETE FROM jilo_agent $sql = 'DELETE FROM jilo_agents
WHERE WHERE
id = :agent_id'; id = :agent_id';
@ -420,13 +420,13 @@ class Agent {
jac.agent_id, jac.agent_id,
jat.description jat.description
FROM FROM
jilo_agent_check jac jilo_agent_checks jac
JOIN JOIN
jilo_agent ja ON jac.agent_id = ja.id jilo_agents ja ON jac.agent_id = ja.id
JOIN JOIN
jilo_agent_type jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN JOIN
host h ON ja.host_id = h.id hosts h ON ja.host_id = h.id
WHERE WHERE
h.id = :host_id h.id = :host_id
AND jat.description = :agent_type AND jat.description = :agent_type
@ -520,13 +520,13 @@ class Agent {
jac.response_content, jac.response_content,
COUNT(*) as checks_count COUNT(*) as checks_count
FROM FROM
jilo_agent_check jac jilo_agent_checks jac
JOIN JOIN
jilo_agent ja ON jac.agent_id = ja.id jilo_agents ja ON jac.agent_id = ja.id
JOIN JOIN
jilo_agent_type jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN JOIN
host h ON ja.host_id = h.id hosts h ON ja.host_id = h.id
WHERE WHERE
h.id = :host_id h.id = :host_id
AND jat.description = :agent_type AND jat.description = :agent_type
@ -591,13 +591,13 @@ class Agent {
jac.timestamp, jac.timestamp,
jac.response_content jac.response_content
FROM FROM
jilo_agent_check jac jilo_agent_checks jac
JOIN JOIN
jilo_agent ja ON jac.agent_id = ja.id jilo_agents ja ON jac.agent_id = ja.id
JOIN JOIN
jilo_agent_type jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN JOIN
host h ON ja.host_id = h.id hosts h ON ja.host_id = h.id
WHERE WHERE
h.id = :host_id h.id = :host_id
AND jat.description = :agent_type AND jat.description = :agent_type

View File

@ -37,7 +37,7 @@ class Host {
platform_id, platform_id,
name name
FROM FROM
host'; hosts';
if ($platform_id !== '' && $host_id !== '') { if ($platform_id !== '' && $host_id !== '') {
$sql .= ' WHERE platform_id = :platform_id AND id = :host_id'; $sql .= ' WHERE platform_id = :platform_id AND id = :host_id';
@ -71,7 +71,7 @@ class Host {
*/ */
public function addHost($newHost) { public function addHost($newHost) {
try { try {
$sql = 'INSERT INTO host $sql = 'INSERT INTO hosts
(address, platform_id, name) (address, platform_id, name)
VALUES VALUES
(:address, :platform_id, :name)'; (:address, :platform_id, :name)';
@ -101,7 +101,7 @@ class Host {
*/ */
public function editHost($platform_id, $updatedHost) { public function editHost($platform_id, $updatedHost) {
try { try {
$sql = 'UPDATE host SET $sql = 'UPDATE hosts SET
address = :address, address = :address,
name = :name name = :name
WHERE WHERE
@ -140,13 +140,13 @@ class Host {
$this->db->beginTransaction(); $this->db->beginTransaction();
// First delete all agents associated with this host // First delete all agents associated with this host
$sql = 'DELETE FROM jilo_agent WHERE host_id = :host_id'; $sql = 'DELETE FROM jilo_agents WHERE host_id = :host_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':host_id', $host_id); $query->bindParam(':host_id', $host_id);
$query->execute(); $query->execute();
// Then delete the host // Then delete the host
$sql = 'DELETE FROM host WHERE id = :host_id'; $sql = 'DELETE FROM hosts WHERE id = :host_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':host_id', $host_id); $query->bindParam(':host_id', $host_id);
$query->execute(); $query->execute();

View File

@ -26,8 +26,8 @@ class PasswordReset {
// Check if email exists // Check if email exists
$query = $this->db->prepare(" $query = $this->db->prepare("
SELECT u.id, um.email SELECT u.id, um.email
FROM user u FROM users u
JOIN user_meta um ON u.id = um.user_id JOIN users_meta um ON u.id = um.user_id
WHERE um.email = :email" WHERE um.email = :email"
); );
$query->bindParam(':email', $email); $query->bindParam(':email', $email);

View File

@ -30,7 +30,7 @@ class Platform {
* @return array An associative array containing platform details. * @return array An associative array containing platform details.
*/ */
public function getPlatformDetails($platform_id = '') { public function getPlatformDetails($platform_id = '') {
$sql = 'SELECT * FROM platform'; $sql = 'SELECT * FROM platforms';
if ($platform_id !== '') { if ($platform_id !== '') {
$sql .= ' WHERE id = :platform_id'; $sql .= ' WHERE id = :platform_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
@ -57,7 +57,7 @@ class Platform {
*/ */
public function addPlatform($newPlatform) { public function addPlatform($newPlatform) {
try { try {
$sql = 'INSERT INTO platform $sql = 'INSERT INTO platforms
(name, jitsi_url, jilo_database) (name, jitsi_url, jilo_database)
VALUES VALUES
(:name, :jitsi_url, :jilo_database)'; (:name, :jitsi_url, :jilo_database)';
@ -90,7 +90,7 @@ class Platform {
*/ */
public function editPlatform($platform_id, $updatedPlatform) { public function editPlatform($platform_id, $updatedPlatform) {
try { try {
$sql = 'UPDATE platform SET $sql = 'UPDATE platforms SET
name = :name, name = :name,
jitsi_url = :jitsi_url, jitsi_url = :jitsi_url,
jilo_database = :jilo_database jilo_database = :jilo_database
@ -125,7 +125,7 @@ class Platform {
$this->db->beginTransaction(); $this->db->beginTransaction();
// First, get all hosts in this platform // First, get all hosts in this platform
$sql = 'SELECT id FROM host WHERE platform_id = :platform_id'; $sql = 'SELECT id FROM hosts WHERE platform_id = :platform_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id); $query->bindParam(':platform_id', $platform_id);
$query->execute(); $query->execute();
@ -133,20 +133,20 @@ class Platform {
// Delete all agents for each host // Delete all agents for each host
foreach ($hosts as $host) { foreach ($hosts as $host) {
$sql = 'DELETE FROM jilo_agent WHERE host_id = :host_id'; $sql = 'DELETE FROM jilo_agents WHERE host_id = :host_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':host_id', $host['id']); $query->bindParam(':host_id', $host['id']);
$query->execute(); $query->execute();
} }
// Delete all hosts in this platform // Delete all hosts in this platform
$sql = 'DELETE FROM host WHERE platform_id = :platform_id'; $sql = 'DELETE FROM hosts WHERE platform_id = :platform_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id); $query->bindParam(':platform_id', $platform_id);
$query->execute(); $query->execute();
// Finally, delete the platform // Finally, delete the platform
$sql = 'DELETE FROM platform WHERE id = :platform_id'; $sql = 'DELETE FROM platforms WHERE id = :platform_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id); $query->bindParam(':platform_id', $platform_id);
$query->execute(); $query->execute();

View File

@ -2,16 +2,15 @@
class RateLimiter { class RateLimiter {
public $db; public $db;
private $database;
private $log; private $log;
public $maxAttempts = 5; // Maximum login attempts public $maxAttempts = 5; // Maximum login attempts
public $decayMinutes = 15; // Time window in minutes public $decayMinutes = 15; // Time window in minutes
public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist
public $autoBlacklistDuration = 24; // Hours to blacklist for public $autoBlacklistDuration = 24; // Hours to blacklist for
public $authRatelimitTable = 'security_rate_auth'; // For rate limiting username/password attempts public $authRatelimitTable = 'login_attempts'; // For username/password attempts
public $pagesRatelimitTable = 'security_rate_page'; // For rate limiting page requests public $pagesRatelimitTable = 'pages_rate_limits'; // For page requests
public $whitelistTable = 'security_ip_whitelist'; // For whitelisting IPs and network ranges public $whitelistTable = 'ip_whitelist';
public $blacklistTable = 'security_ip_blacklist'; // For blacklisting IPs and network ranges public $blacklistTable = 'ip_blacklist';
private $pageLimits = [ private $pageLimits = [
// Default rate limits per minute // Default rate limits per minute
'default' => 60, 'default' => 60,
@ -24,8 +23,11 @@ class RateLimiter {
]; ];
public function __construct($database) { public function __construct($database) {
$this->database = $database; // Store the Database object if ($database instanceof PDO) {
$this->db = $database->getConnection(); $this->db = $database;
} else {
$this->db = $database->getConnection();
}
// Initialize logger via Log wrapper // Initialize logger via Log wrapper
require_once __DIR__ . '/log.php'; require_once __DIR__ . '/log.php';
$this->log = new Log($database); $this->log = new Log($database);
@ -37,47 +39,42 @@ class RateLimiter {
private function createTablesIfNotExist() { private function createTablesIfNotExist() {
// Authentication attempts table // Authentication attempts table
$sql = "CREATE TABLE IF NOT EXISTS {$this->authRatelimitTable} ( $sql = "CREATE TABLE IF NOT EXISTS {$this->authRatelimitTable} (
id int(11) PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address VARCHAR(45) NOT NULL, ip_address TEXT NOT NULL,
username VARCHAR(255) NOT NULL, username TEXT NOT NULL,
attempted_at DATETIME DEFAULT CURRENT_TIMESTAMP, attempted_at TEXT DEFAULT (DATETIME('now'))
INDEX idx_ip_username (ip_address, username)
)"; )";
$this->db->exec($sql); $this->db->exec($sql);
// Pages rate limits table // Pages rate limits table
$sql = "CREATE TABLE IF NOT EXISTS {$this->pagesRatelimitTable} ( $sql = "CREATE TABLE IF NOT EXISTS {$this->pagesRatelimitTable} (
id int(11) PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address VARCHAR(45) NOT NULL, ip_address TEXT NOT NULL,
endpoint VARCHAR(255) NOT NULL, endpoint TEXT NOT NULL,
request_time DATETIME DEFAULT CURRENT_TIMESTAMP, request_time DATETIME DEFAULT CURRENT_TIMESTAMP
INDEX idx_ip_endpoint (ip_address, endpoint),
INDEX idx_request_time (request_time)
)"; )";
$this->db->exec($sql); $this->db->exec($sql);
// IP whitelist table // IP whitelist table
$sql = "CREATE TABLE IF NOT EXISTS {$this->whitelistTable} ( $sql = "CREATE TABLE IF NOT EXISTS {$this->whitelistTable} (
id int(11) PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address VARCHAR(45) NOT NULL, ip_address TEXT NOT NULL UNIQUE,
is_network BOOLEAN DEFAULT FALSE, is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
description VARCHAR(255), description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT (DATETIME('now')),
created_by VARCHAR(255), created_by TEXT
UNIQUE KEY unique_ip (ip_address)
)"; )";
$this->db->exec($sql); $this->db->exec($sql);
// IP blacklist table // IP blacklist table
$sql = "CREATE TABLE IF NOT EXISTS {$this->blacklistTable} ( $sql = "CREATE TABLE IF NOT EXISTS {$this->blacklistTable} (
id int(11) PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address VARCHAR(45) NOT NULL, ip_address TEXT NOT NULL UNIQUE,
is_network BOOLEAN DEFAULT FALSE, is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
reason VARCHAR(255), reason TEXT,
expiry_time TIMESTAMP NULL, expiry_time TEXT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT (DATETIME('now')),
created_by VARCHAR(255), created_by TEXT
UNIQUE KEY unique_ip (ip_address)
)"; )";
$this->db->exec($sql); $this->db->exec($sql);
@ -91,7 +88,7 @@ class RateLimiter {
]; ];
// Insert default whitelisted IPs if they don't exist // Insert default whitelisted IPs if they don't exist
$stmt = $this->db->prepare("INSERT IGNORE INTO {$this->whitelistTable} $stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->whitelistTable}
(ip_address, is_network, description, created_by) (ip_address, is_network, description, created_by)
VALUES (?, ?, ?, 'system')"); VALUES (?, ?, ?, 'system')");
foreach ($defaultIps as $ip) { foreach ($defaultIps as $ip) {
@ -107,7 +104,7 @@ class RateLimiter {
['203.0.113.0/24', true, 'TEST-NET-3 Documentation space - RFC 5737'] ['203.0.113.0/24', true, 'TEST-NET-3 Documentation space - RFC 5737']
]; ];
$stmt = $this->db->prepare("INSERT IGNORE INTO {$this->blacklistTable} $stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->blacklistTable}
(ip_address, is_network, reason, created_by) (ip_address, is_network, reason, created_by)
VALUES (?, ?, ?, 'system')"); VALUES (?, ?, ?, 'system')");
@ -122,7 +119,7 @@ class RateLimiter {
*/ */
public function getRecentAttempts($ip) { public function getRecentAttempts($ip) {
$stmt = $this->db->prepare("SELECT COUNT(*) as attempts FROM {$this->authRatelimitTable} $stmt = $this->db->prepare("SELECT COUNT(*) as attempts FROM {$this->authRatelimitTable}
WHERE ip_address = ? AND attempted_at > DATE_SUB(NOW(), INTERVAL ? MINUTE)"); WHERE ip_address = ? AND attempted_at > datetime('now', '-' || :minutes || ' minutes')");
$stmt->execute([$ip, $this->decayMinutes]); $stmt->execute([$ip, $this->decayMinutes]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
return intval($result['attempts']); return intval($result['attempts']);
@ -225,13 +222,9 @@ class RateLimiter {
return false; return false;
} }
$stmt = $this->db->prepare("INSERT INTO {$this->whitelistTable} $stmt = $this->db->prepare("INSERT OR REPLACE INTO {$this->whitelistTable}
(ip_address, is_network, description, created_by) (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]); $result = $stmt->execute([$ip, $isNetwork, $description, $createdBy]);
@ -243,7 +236,7 @@ class RateLimiter {
$createdBy, $createdBy,
$description $description
); );
$this->log->insertLog($userId ?? null, $logMessage, 'system'); $this->log->insertLog($userId ?? 0, $logMessage, 'system');
} }
return $result; return $result;
@ -278,7 +271,7 @@ class RateLimiter {
$removedBy, $removedBy,
$ipDetails['created_by'] $ipDetails['created_by']
); );
$this->log->insertLog($userId ?? null, $logMessage, 'system'); $this->log->insertLog($userId ?? 0, $logMessage, 'system');
} }
return $result; return $result;
@ -306,14 +299,9 @@ class RateLimiter {
$expiryTime = $expiryHours ? date('Y-m-d H:i:s', strtotime("+{$expiryHours} hours")) : null; $expiryTime = $expiryHours ? date('Y-m-d H:i:s', strtotime("+{$expiryHours} hours")) : null;
$stmt = $this->db->prepare("INSERT INTO {$this->blacklistTable} $stmt = $this->db->prepare("INSERT OR REPLACE INTO {$this->blacklistTable}
(ip_address, is_network, reason, expiry_time, created_by) (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]); $result = $stmt->execute([$ip, $isNetwork, $reason, $expiryTime, $createdBy]);
@ -326,7 +314,7 @@ class RateLimiter {
$reason, $reason,
$expiryTime ?? 'never' $expiryTime ?? 'never'
); );
$this->log->insertLog($userId ?? null, $logMessage, 'system'); $this->log->insertLog($userId ?? 0, $logMessage, 'system');
} }
return $result; return $result;
@ -360,7 +348,7 @@ class RateLimiter {
$ipDetails['created_by'], $ipDetails['created_by'],
$ipDetails['reason'] $ipDetails['reason']
); );
$this->log->insertLog($userId ?? null, $logMessage, 'system'); $this->log->insertLog($userId ?? 0, $logMessage, 'system');
} }
return $result; return $result;
@ -391,17 +379,17 @@ class RateLimiter {
try { try {
// Remove expired blacklist entries // Remove expired blacklist entries
$stmt = $this->db->prepare("DELETE FROM {$this->blacklistTable} $stmt = $this->db->prepare("DELETE FROM {$this->blacklistTable}
WHERE expiry_time IS NOT NULL AND expiry_time < NOW()"); WHERE expiry_time IS NOT NULL AND expiry_time < datetime('now')");
$stmt->execute(); $stmt->execute();
// Clean old login attempts // Clean old login attempts
$stmt = $this->db->prepare("DELETE FROM {$this->authRatelimitTable} $stmt = $this->db->prepare("DELETE FROM {$this->authRatelimitTable}
WHERE attempted_at < DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"); WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')");
$stmt->execute([':minutes' => $this->decayMinutes]); $stmt->execute([':minutes' => $this->decayMinutes]);
return true; return true;
} catch (Exception $e) { } catch (Exception $e) {
$this->log->insertLog(null, "Failed to cleanup expired entries: " . $e->getMessage(), 'system'); $this->log->insertLog(0, "Failed to cleanup expired entries: " . $e->getMessage(), 'system');
Feedback::flash('ERROR', 'DEFAULT', "Failed to cleanup expired entries: " . $e->getMessage()); Feedback::flash('ERROR', 'DEFAULT', "Failed to cleanup expired entries: " . $e->getMessage());
return false; return false;
} }
@ -430,7 +418,7 @@ class RateLimiter {
$sql = "SELECT COUNT(*) as total_attempts $sql = "SELECT COUNT(*) as total_attempts
FROM {$this->authRatelimitTable} FROM {$this->authRatelimitTable}
WHERE ip_address = :ip WHERE ip_address = :ip
AND attempted_at > DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; AND attempted_at > datetime('now', '-' || :minutes || ' minutes')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([ $stmt->execute([
':ip' => $ipAddress, ':ip' => $ipAddress,
@ -468,7 +456,7 @@ class RateLimiter {
FROM {$this->authRatelimitTable} FROM {$this->authRatelimitTable}
WHERE ip_address = :ip WHERE ip_address = :ip
AND username = :username AND username = :username
AND attempted_at > DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; AND attempted_at > datetime('now', '-' || :minutes || ' minutes')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([ $stmt->execute([
@ -504,7 +492,7 @@ class RateLimiter {
public function clearOldAttempts() { public function clearOldAttempts() {
$sql = "DELETE FROM {$this->authRatelimitTable} $sql = "DELETE FROM {$this->authRatelimitTable}
WHERE attempted_at < DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([ $stmt->execute([
@ -517,7 +505,7 @@ class RateLimiter {
FROM {$this->authRatelimitTable} FROM {$this->authRatelimitTable}
WHERE ip_address = :ip WHERE ip_address = :ip
AND username = :username AND username = :username
AND attempted_at > DATE_SUB(NOW(), INTERVAL :minutes MINUTE)"; AND attempted_at > datetime('now', '-' || :minutes || ' minutes')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([ $stmt->execute([
@ -559,7 +547,7 @@ class RateLimiter {
FROM {$this->pagesRatelimitTable} FROM {$this->pagesRatelimitTable}
WHERE ip_address = :ip WHERE ip_address = :ip
AND endpoint = :endpoint AND endpoint = :endpoint
AND request_time >= DATE_SUB(NOW(), INTERVAL 1 MINUTE)"; AND request_time >= DATETIME('now', '-1 minute')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([ $stmt->execute([
@ -590,7 +578,7 @@ class RateLimiter {
*/ */
private function cleanOldPageRequests() { private function cleanOldPageRequests() {
$sql = "DELETE FROM {$this->pagesRatelimitTable} $sql = "DELETE FROM {$this->pagesRatelimitTable}
WHERE request_time < DATE_SUB(NOW(), INTERVAL 1 MINUTE)"; WHERE request_time < DATETIME('now', '-1 minute')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute(); $stmt->execute();
@ -602,10 +590,8 @@ class RateLimiter {
private function getPageLimitForEndpoint($endpoint, $userId = null) { private function getPageLimitForEndpoint($endpoint, $userId = null) {
// Admin users get higher limits // Admin users get higher limits
if ($userId) { if ($userId) {
// Check admin rights directly from database $userObj = new User($this->db);
$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 = ?'); if ($userObj->hasRight($userId, 'admin')) {
$stmt->execute([$userId, 'superuser']);
if ($stmt->fetchColumn() > 0) {
return $this->pageLimits['admin']; return $this->pageLimits['admin'];
} }
} }
@ -641,7 +627,7 @@ class RateLimiter {
FROM {$this->pagesRatelimitTable} FROM {$this->pagesRatelimitTable}
WHERE ip_address = :ip WHERE ip_address = :ip
AND endpoint = :endpoint AND endpoint = :endpoint
AND request_time > DATE_SUB(NOW(), INTERVAL 1 MINUTE)"; AND request_time > DATETIME('now', '-1 minute')";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
$stmt->execute([ $stmt->execute([

View File

@ -3,7 +3,7 @@
/** /**
* class User * class User
* *
* Handles user-related functionalities such as login, rights management, and profile updates. * Handles user-related functionalities such as registration, login, rights management, and profile updates.
*/ */
class User { class User {
/** /**
@ -36,9 +36,9 @@ class User {
/** /**
* Logs in a user by verifying credentials. * Logs in a user by verifying credentials.
* *
* @param string $username The username of the user. * @param string $username The username of the user.
* @param string $password The password of the user. * @param string $password The password of the user.
* @param string $twoFactorCode Optional. The 2FA code if 2FA is enabled. * @param string $twoFactorCode Optional. The 2FA code if 2FA is enabled.
* *
* @return array Login result with status and any necessary data * @return array Login result with status and any necessary data
*/ */
@ -53,7 +53,7 @@ class User {
} }
// Then check credentials // Then check credentials
$query = $this->db->prepare("SELECT * FROM user WHERE username = :username"); $query = $this->db->prepare("SELECT * FROM users WHERE username = :username");
$query->bindParam(':username', $username); $query->bindParam(':username', $username);
$query->execute(); $query->execute();
@ -92,10 +92,7 @@ class User {
// Get remaining attempts AFTER this failed attempt // Get remaining attempts AFTER this failed attempt
$remainingAttempts = $this->rateLimiter->getRemainingAttempts($username, $ipAddress); $remainingAttempts = $this->rateLimiter->getRemainingAttempts($username, $ipAddress);
return [ throw new Exception("Invalid credentials. {$remainingAttempts} attempts remaining.");
'status' => 'failed',
'message' => "Invalid credentials. {$remainingAttempts} attempts remaining."
];
} }
@ -108,7 +105,7 @@ class User {
*/ */
// FIXME not used now? // FIXME not used now?
public function getUserId($username) { public function getUserId($username) {
$sql = 'SELECT id FROM user WHERE username = :username'; $sql = 'SELECT id FROM users WHERE username = :username';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':username', $username); $query->bindParam(':username', $username);
@ -131,8 +128,8 @@ class User {
um.*, um.*,
u.username u.username
FROM FROM
user_meta um users_meta um
LEFT JOIN user u LEFT JOIN users u
ON um.user_id = u.id ON um.user_id = u.id
WHERE WHERE
u.id = :user_id'; u.id = :user_id';
@ -156,7 +153,7 @@ class User {
* @return void * @return void
*/ */
public function addUserRight($userId, $right_id) { public function addUserRight($userId, $right_id) {
$sql = 'INSERT INTO user_right $sql = 'INSERT INTO users_rights
(user_id, right_id) (user_id, right_id)
VALUES VALUES
(:user_id, :right_id)'; (:user_id, :right_id)';
@ -177,7 +174,7 @@ class User {
* @return void * @return void
*/ */
public function removeUserRight($userId, $right_id) { public function removeUserRight($userId, $right_id) {
$sql = 'DELETE FROM user_right $sql = 'DELETE FROM users_rights
WHERE WHERE
user_id = :user_id user_id = :user_id
AND AND
@ -199,7 +196,7 @@ class User {
$sql = 'SELECT $sql = 'SELECT
id AS right_id, id AS right_id,
name AS right_name name AS right_name
FROM `right` FROM rights
ORDER BY id ASC'; ORDER BY id ASC';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute(); $query->execute();
@ -222,10 +219,10 @@ class User {
r.id AS right_id, r.id AS right_id,
r.name AS right_name r.name AS right_name
FROM FROM
`user` u users u
LEFT JOIN `user_right` ur LEFT JOIN users_rights ur
ON u.id = ur.user_id ON u.id = ur.user_id
LEFT JOIN `right` r LEFT JOIN rights r
ON ur.right_id = r.id ON ur.right_id = r.id
WHERE WHERE
u.id = :user_id'; u.id = :user_id';
@ -315,7 +312,7 @@ class User {
*/ */
public function editUser($userId, $updatedUser) { public function editUser($userId, $updatedUser) {
try { try {
$sql = 'UPDATE user_meta SET $sql = 'UPDATE users_meta SET
name = :name, name = :name,
email = :email, email = :email,
timezone = :timezone, timezone = :timezone,
@ -350,7 +347,7 @@ class User {
public function removeAvatar($userId, $old_avatar = '') { public function removeAvatar($userId, $old_avatar = '') {
try { try {
// remove from database // remove from database
$sql = 'UPDATE user_meta SET $sql = 'UPDATE users_meta SET
avatar = NULL avatar = NULL
WHERE user_id = :user_id'; WHERE user_id = :user_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
@ -399,7 +396,7 @@ class User {
if (move_uploaded_file($fileTmpPath, $dest_path)) { if (move_uploaded_file($fileTmpPath, $dest_path)) {
try { try {
// update user's avatar path in DB // update user's avatar path in DB
$sql = 'UPDATE user_meta SET $sql = 'UPDATE users_meta SET
avatar = :avatar avatar = :avatar
WHERE user_id = :user_id'; WHERE user_id = :user_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
@ -435,7 +432,7 @@ class User {
*/ */
public function getUsers() { public function getUsers() {
$sql = "SELECT id, username $sql = "SELECT id, username
FROM `user` FROM users
ORDER BY username ASC"; ORDER BY username ASC";
$stmt = $this->db->prepare($sql); $stmt = $this->db->prepare($sql);
@ -498,7 +495,7 @@ class User {
public function changePassword($userId, $currentPassword, $newPassword) { public function changePassword($userId, $currentPassword, $newPassword) {
try { try {
// First verify the current password // First verify the current password
$sql = "SELECT password FROM user WHERE id = :user_id"; $sql = "SELECT password FROM users WHERE id = :user_id";
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute([':user_id' => $userId]); $query->execute([':user_id' => $userId]);
$user = $query->fetch(PDO::FETCH_ASSOC); $user = $query->fetch(PDO::FETCH_ASSOC);
@ -511,7 +508,7 @@ class User {
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT); $hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
// Update the password // Update the password
$sql = "UPDATE user SET password = :password WHERE id = :user_id"; $sql = "UPDATE users SET password = :password WHERE id = :user_id";
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
return $query->execute([ return $query->execute([
':password' => $hashedPassword, ':password' => $hashedPassword,

View File

@ -22,20 +22,12 @@ return [
//******************************************* //*******************************************
// database // database
'db_type' => 'mariadb', 'db' => [
// DB type for the web app, currently only "sqlite" is used
'sqlite' => [ 'db_type' => 'sqlite',
// default is ../app/jilo-web.db
'sqlite_file' => '../app/jilo-web.db', '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
'avatars_path' => 'uploads/avatars/', 'avatars_path' => 'uploads/avatars/',
// default avatar // default avatar

View File

@ -21,10 +21,10 @@ class DatabaseConnector
try { try {
$db = connectDB($config); $db = connectDB($config);
if (!$db) { if (!$db['db']) {
throw new Exception('Could not connect to database'); throw new Exception('Could not connect to database');
} }
return $db; return $db['db'];
} catch (Exception $e) { } catch (Exception $e) {
// Show error and exit // Show error and exit
Feedback::flash('ERROR', 'DEFAULT', getError('Error connecting to the database.', $e->getMessage())); Feedback::flash('ERROR', 'DEFAULT', getError('Error connecting to the database.', $e->getMessage()));

View File

@ -1,63 +1,61 @@
<?php <?php
// connect to database // connect to database
function connectDB($config) { function connectDB($config, $database = '', $dbFile = '', $platformId = '') {
// sqlite database file
if ($config['db_type'] === 'sqlite') { // connecting ti a jilo sqlite database
if ($database === 'jilo') {
try { try {
$dbFile = $config['sqlite']['sqlite_file'] ?? null;
if (!$dbFile || !file_exists($dbFile)) { if (!$dbFile || !file_exists($dbFile)) {
throw new Exception(getError("Database file \"{$dbFile}\"not found.")); throw new Exception(getError("Invalid platform ID \"{$platformId}\", database file \"{$dbFile}\" not found."));
} }
$db = new Database([ $db = new Database([
'type' => $config['db_type'], 'type' => 'sqlite',
'dbFile' => $dbFile, 'dbFile' => $dbFile,
]); ]);
$pdo = $db->getConnection(); return ['db' => $db, 'error' => null];
} catch (Exception $e) { } catch (Exception $e) {
Feedback::flash('ERROR', 'DEFAULT', getError('Error connecting to DB.', $e->getMessage())); return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
return false;
} }
return $db;
// mysql/mariadb database // connecting to a jilo-web database of the web app
} 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 { } else {
Feedback::flash('ERROR', 'DEFAULT', getError("Error: unknown database type \"{$config['db_type']}\""));
return false;
}
} // sqlite database file
if ($config['db']['db_type'] === 'sqlite') {
// connect to Jilo database try {
function connectJiloDB($config, $dbFile = '', $platformId = '') { $db = new Database([
try { 'type' => $config['db']['db_type'],
if (!$dbFile || !file_exists($dbFile)) { 'dbFile' => $config['db']['sqlite_file'],
throw new Exception(getError("Invalid platform ID \"{$platformId}\", database file \"{$dbFile}\" not found.")); ]);
$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();
} }
$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())];
} }
} }

View File

@ -19,8 +19,8 @@ $agentId = filter_input(INPUT_GET, 'agent', FILTER_VALIDATE_INT);
require '../app/classes/agent.php'; require '../app/classes/agent.php';
require '../app/classes/host.php'; require '../app/classes/host.php';
$agentObject = new Agent($db); $agentObject = new Agent($dbWeb);
$hostObject = new Host($db); $hostObject = new Host($dbWeb);
/** /**
* Get the cache key for an agent * Get the cache key for an agent
@ -50,7 +50,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Apply rate limiting for adding new contacts // Apply rate limiting for adding new contacts
require '../app/includes/rate_limit_middleware.php'; require '../app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'contact', $userId); checkRateLimit($dbWeb, 'contact', $userId);
// Validate agent ID for POST operations // Validate agent ID for POST operations
if ($agentId === false || $agentId === null) { if ($agentId === false || $agentId === null) {

View File

@ -9,7 +9,7 @@
*/ */
// connect to database // connect to database
$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); $response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// if DB connection has error, display it and stop here // if DB connection has error, display it and stop here
if ($response['db'] === null) { if ($response['db'] === null) {

View File

@ -9,7 +9,7 @@
*/ */
// connect to database // connect to database
$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); $response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// if DB connection has error, display it and stop here // if DB connection has error, display it and stop here
if ($response['db'] === null) { if ($response['db'] === null) {

View File

@ -13,7 +13,7 @@ require '../app/classes/config.php';
require '../app/classes/api_response.php'; require '../app/classes/api_response.php';
// Initialize required objects // Initialize required objects
$userObject = new User($db); $userObject = new User($dbWeb);
$configObject = new Config(); $configObject = new Config();
// For AJAX requests // For AJAX requests
@ -63,7 +63,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Apply rate limiting // Apply rate limiting
require '../app/includes/rate_limit_middleware.php'; require '../app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'config', $userId); checkRateLimit($dbWeb, 'config', $userId);
// Ensure no output before this point // Ensure no output before this point
ob_clean(); ob_clean();

View File

@ -15,7 +15,7 @@
*/ */
// Initialize user object // Initialize user object
$userObject = new User($db); $userObject = new User($dbWeb);
// Get action and item from request // Get action and item from request
$action = $_REQUEST['action'] ?? ''; $action = $_REQUEST['action'] ?? '';
@ -33,7 +33,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Apply rate limiting // Apply rate limiting
require_once '../app/includes/rate_limit_middleware.php'; require_once '../app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'credentials', $userId); checkRateLimit($dbWeb, 'credentials', $userId);
switch ($item) { switch ($item) {
case '2fa': case '2fa':

View File

@ -16,7 +16,7 @@ require '../app/classes/conference.php';
require '../app/classes/participant.php'; require '../app/classes/participant.php';
// connect to database // connect to database
$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); $response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// if DB connection has error, display it and stop here // if DB connection has error, display it and stop here
if ($response['db'] === null) { if ($response['db'] === null) {

View File

@ -7,11 +7,11 @@ require '../app/classes/agent.php';
require '../app/classes/conference.php'; require '../app/classes/conference.php';
require '../app/classes/host.php'; require '../app/classes/host.php';
$agentObject = new Agent($db); $agentObject = new Agent($dbWeb);
$hostObject = new Host($db); $hostObject = new Host($dbWeb);
// Connect to Jilo database for log data // Connect to Jilo database for log data
$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); $response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
if ($response['db'] === null) { if ($response['db'] === null) {
Feedback::flash('ERROR', 'DEFAULT', $response['error']); Feedback::flash('ERROR', 'DEFAULT', $response['error']);
} else { } else {

View File

@ -3,8 +3,8 @@
require '../app/classes/agent.php'; require '../app/classes/agent.php';
require '../app/classes/host.php'; require '../app/classes/host.php';
$agentObject = new Agent($db); $agentObject = new Agent($dbWeb);
$hostObject = new Host($db); $hostObject = new Host($dbWeb);
// Define metrics to display // Define metrics to display
$metrics = [ $metrics = [

View File

@ -19,7 +19,7 @@ unset($error);
try { try {
// connect to database // connect to database
$db = connectDB($config); $db = connectDB($config)['db'];
// Initialize RateLimiter // Initialize RateLimiter
require_once '../app/classes/ratelimiter.php'; require_once '../app/classes/ratelimiter.php';

View File

@ -9,7 +9,7 @@
*/ */
// connect to database // connect to database
$response = connectJiloDB($config, $platformDetails[0]['jilo_database'], $platform_id); $response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// if DB connection has error, display it and stop here // if DB connection has error, display it and stop here
if ($response['db'] === null) { if ($response['db'] === null) {

View File

@ -30,7 +30,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Apply rate limiting for profile operations // Apply rate limiting for profile operations
require_once '../app/includes/rate_limit_middleware.php'; require_once '../app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'profile', $userId); checkRateLimit($dbWeb, 'profile', $userId);
// avatar removal // avatar removal
if ($item === 'avatar' && $action === 'remove') { if ($item === 'avatar' && $action === 'remove') {

View File

@ -14,7 +14,7 @@ $section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section'
// Initialize RateLimiter // Initialize RateLimiter
require_once '../app/classes/ratelimiter.php'; require_once '../app/classes/ratelimiter.php';
$rateLimiter = new RateLimiter($db); $rateLimiter = new RateLimiter($dbWeb);
// Handle form submissions // Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { 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 // Apply rate limiting for security operations
require_once '../app/includes/rate_limit_middleware.php'; require_once '../app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'security', $userId); checkRateLimit($dbWeb, 'security', $userId);
$action = $_POST['action']; $action = $_POST['action'];
$validator = new Validator($_POST); $validator = new Validator($_POST);

View File

@ -21,8 +21,8 @@ $host = $_REQUEST['host'] ?? '';
require '../app/classes/host.php'; require '../app/classes/host.php';
require '../app/classes/agent.php'; require '../app/classes/agent.php';
$hostObject = new Host($db); $hostObject = new Host($dbWeb);
$agentObject = new Agent($db); $agentObject = new Agent($dbWeb);
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
/** /**
@ -31,7 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Apply rate limiting for profile operations // Apply rate limiting for profile operations
require_once '../app/includes/rate_limit_middleware.php'; require_once '../app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'profile', $userId); checkRateLimit($dbWeb, 'profile', $userId);
// Get hash from URL if present // Get hash from URL if present
$hash = parse_url($_SERVER['REQUEST_URI'], PHP_URL_FRAGMENT) ?? ''; $hash = parse_url($_SERVER['REQUEST_URI'], PHP_URL_FRAGMENT) ?? '';

View File

@ -13,8 +13,8 @@ include '../app/helpers/feedback.php';
require '../app/classes/agent.php'; require '../app/classes/agent.php';
require '../app/classes/host.php'; require '../app/classes/host.php';
$agentObject = new Agent($db); $agentObject = new Agent($dbWeb);
$hostObject = new Host($db); $hostObject = new Host($dbWeb);
include '../app/templates/status-server.php'; include '../app/templates/status-server.php';
@ -22,7 +22,7 @@ include '../app/templates/status-server.php';
foreach ($platformsAll as $platform) { foreach ($platformsAll as $platform) {
// check if we can connect to the jilo database // check if we can connect to the jilo database
$response = connectJiloDB($config, $platform['jilo_database'], $platform['id']); $response = connectDB($config, 'jilo', $platform['jilo_database'], $platform['id']);
if ($response['error'] !== null) { if ($response['error'] !== null) {
$jilo_database_status = $response['error']; $jilo_database_status = $response['error'];
} else { } else {

View File

@ -11,7 +11,7 @@ SET NAMES utf8mb4;
-- --
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `user` ( CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL, `username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL, `password` varchar(100) NOT NULL,
@ -19,12 +19,12 @@ CREATE TABLE `user` (
UNIQUE KEY `username` (`username`) UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
INSERT INTO `user` (`id`, `username`, `password`) VALUES INSERT INTO `users` (`id`, `username`, `password`) VALUES
(1,'demo','$2y$12$AtIKs3eVxD4wTT1IWwJujuuHyGhhmfBJYqSfIrPFFPMDfKu3Rcsx6'), (1,'demo','$2y$10$tLCLvgYu91gf/zBoc58Am.iVls/SOMcIXO3ykGfgFFei9yneZTrb2'),
(2,'demo1','$2y$12$ELwYyhQ8XDkVvX9Xsb0mlORqeQHNFaBOvaBuPQym4n4IomA/DgvLC'); (2,'demo1','$2y$10$LtV9m.rMCJ.K/g45e6tzDexZ8C/9xxu3qFCkvz92pUYa7Jg06np0i');
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `user_meta` ( CREATE TABLE `users_meta` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL, `user_id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL,
@ -34,22 +34,22 @@ CREATE TABLE `user_meta` (
`bio` text DEFAULT NULL, `bio` text DEFAULT NULL,
PRIMARY KEY (`id`,`user_id`) USING BTREE, PRIMARY KEY (`id`,`user_id`) USING BTREE,
KEY `user_id` (`user_id`), KEY `user_id` (`user_id`),
CONSTRAINT `user_meta_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `user_meta_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
INSERT INTO `user_meta` (`id`, `user_id`, `name`, `email`, `timezone`, `avatar`, `bio`) VALUES INSERT INTO `users_meta` (`id`, `user_id`, `name`, `email`, `timezone`, `avatar`, `bio`) VALUES
(1,1,'demo admin user','admin@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web'), (1,1,'demo admin user','admin@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web'),
(2,2,'demo user','demo@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web'); (2,2,'demo user','demo@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web');
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `right` ( CREATE TABLE `rights` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL, `name` varchar(255) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
INSERT INTO `right` (`id`, `name`) VALUES INSERT INTO `rights` (`id`, `name`) VALUES
(1, 'superuser'), (1, 'superuser'),
(2, 'edit users'), (2, 'edit users'),
(3, 'view config file'), (3, 'view config file'),
@ -67,13 +67,13 @@ INSERT INTO `right` (`id`, `name`) VALUES
(15,'view jilo config'); (15,'view jilo config');
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `user_right` ( CREATE TABLE `users_right` (
`user_id` int(11) NOT NULL, `user_id` int(11) NOT NULL,
`right_id` int(11) NOT NULL, `right_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`right_id`), PRIMARY KEY (`user_id`,`right_id`),
KEY `fk_right_id` (`right_id`), KEY `fk_right_id` (`right_id`),
CONSTRAINT `fk_right_id` FOREIGN KEY (`right_id`) REFERENCES `right` (`id`), CONSTRAINT `fk_right_id` FOREIGN KEY (`right_id`) REFERENCES `rights` (`id`),
CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -85,7 +85,7 @@ CREATE TABLE `user_2fa` (
`created_at` datetime NOT NULL, `created_at` datetime NOT NULL,
`last_used` datetime DEFAULT NULL, `last_used` datetime DEFAULT NULL,
PRIMARY KEY (`user_id`), PRIMARY KEY (`user_id`),
CONSTRAINT `fk_user_2fa_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `fk_user_2fa_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -95,7 +95,7 @@ CREATE TABLE `user_2fa_temp` (
`created_at` datetime NOT NULL, `created_at` datetime NOT NULL,
`expires_at` datetime NOT NULL, `expires_at` datetime NOT NULL,
PRIMARY KEY (`user_id`, `code`), PRIMARY KEY (`user_id`, `code`),
CONSTRAINT `fk_user_2fa_temp_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `fk_user_2fa_temp_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
@ -106,7 +106,7 @@ CREATE TABLE `user_password_reset` (
`expires` int(11) NOT NULL, `expires` int(11) NOT NULL,
`used` TINYINT(1) NOT NULL DEFAULT 0, `used` TINYINT(1) NOT NULL DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT `fk_user_password_reset` FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), CONSTRAINT `fk_user_password_reset` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`),
UNIQUE KEY `token_idx` (`token`) UNIQUE KEY `token_idx` (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
@ -115,7 +115,7 @@ CREATE TABLE `user_password_reset` (
-- --
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `security_rate_auth` ( CREATE TABLE `login_attempts` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip_address` varchar(45) NOT NULL, `ip_address` varchar(45) NOT NULL,
`username` varchar(255) NOT NULL, `username` varchar(255) NOT NULL,
@ -125,7 +125,7 @@ CREATE TABLE `security_rate_auth` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `security_rate_page` ( CREATE TABLE `pages_rate_limits` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip_address` varchar(45) NOT NULL, `ip_address` varchar(45) NOT NULL,
`endpoint` varchar(255) NOT NULL, `endpoint` varchar(255) NOT NULL,
@ -136,7 +136,7 @@ CREATE TABLE `security_rate_page` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `security_ip_blacklist` ( CREATE TABLE `ip_blacklist` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip_address` varchar(45) NOT NULL, `ip_address` varchar(45) NOT NULL,
`is_network` tinyint(1) DEFAULT 0, `is_network` tinyint(1) DEFAULT 0,
@ -148,7 +148,7 @@ CREATE TABLE `security_ip_blacklist` (
UNIQUE KEY `unique_ip` (`ip_address`) UNIQUE KEY `unique_ip` (`ip_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
INSERT INTO `security_ip_blacklist` (`id`, `ip_address`, `is_network`, `reason`, `expiry_time`, `created_at`, `created_by`) VALUES INSERT INTO `ip_blacklist` (`id`, `ip_address`, `is_network`, `reason`, `expiry_time`, `created_at`, `created_by`) VALUES
(1, '0.0.0.0/8', 1, 'Reserved address space - RFC 1122', NULL, '2025-01-03 16:40:15', 'system'), (1, '0.0.0.0/8', 1, 'Reserved address space - RFC 1122', NULL, '2025-01-03 16:40:15', 'system'),
(2, '100.64.0.0/10', 1, 'Carrier-grade NAT space - RFC 6598', NULL, '2025-01-03 16:40:15', 'system'), (2, '100.64.0.0/10', 1, 'Carrier-grade NAT space - RFC 6598', NULL, '2025-01-03 16:40:15', 'system'),
(3, '192.0.2.0/24', 1, 'TEST-NET-1 Documentation space - RFC 5737', NULL, '2025-01-03 16:40:15', 'system'), (3, '192.0.2.0/24', 1, 'TEST-NET-1 Documentation space - RFC 5737', NULL, '2025-01-03 16:40:15', 'system'),
@ -156,7 +156,7 @@ INSERT INTO `security_ip_blacklist` (`id`, `ip_address`, `is_network`, `reason`,
(5, '203.0.113.0/24', 1, 'TEST-NET-3 Documentation space - RFC 5737', NULL, '2025-01-03 16:40:15', 'system'); (5, '203.0.113.0/24', 1, 'TEST-NET-3 Documentation space - RFC 5737', NULL, '2025-01-03 16:40:15', 'system');
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `security_ip_whitelist` ( CREATE TABLE `ip_whitelist` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip_address` varchar(45) NOT NULL, `ip_address` varchar(45) NOT NULL,
`is_network` tinyint(1) DEFAULT 0, `is_network` tinyint(1) DEFAULT 0,
@ -167,7 +167,7 @@ CREATE TABLE `security_ip_whitelist` (
UNIQUE KEY `unique_ip` (`ip_address`) UNIQUE KEY `unique_ip` (`ip_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
INSERT INTO `security_ip_whitelist` (`id`, `ip_address`, `is_network`, `description`, `created_at`, `created_by`) VALUES INSERT INTO `ip_whitelist` (`id`, `ip_address`, `is_network`, `description`, `created_at`, `created_by`) VALUES
(1, '127.0.0.1', 0, 'localhost IPv4', '2025-01-03 16:40:15', 'system'), (1, '127.0.0.1', 0, 'localhost IPv4', '2025-01-03 16:40:15', 'system'),
(2, '::1', 0, 'localhost IPv6', '2025-01-03 16:40:15', 'system'), (2, '::1', 0, 'localhost IPv6', '2025-01-03 16:40:15', 'system'),
(3, '10.0.0.0/8', 1, 'Private network (Class A)', '2025-01-03 16:40:15', 'system'), (3, '10.0.0.0/8', 1, 'Private network (Class A)', '2025-01-03 16:40:15', 'system'),
@ -179,7 +179,7 @@ INSERT INTO `security_ip_whitelist` (`id`, `ip_address`, `is_network`, `descript
-- --
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `log` ( CREATE TABLE `logs` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL, `user_id` int(11) NOT NULL,
`time` datetime NOT NULL DEFAULT current_timestamp(), `time` datetime NOT NULL DEFAULT current_timestamp(),
@ -187,7 +187,7 @@ CREATE TABLE `log` (
`message` varchar(255) NOT NULL, `message` varchar(255) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `user_id` (`user_id`), KEY `user_id` (`user_id`),
CONSTRAINT `log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) CONSTRAINT `log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- --
@ -195,13 +195,13 @@ CREATE TABLE `log` (
-- --
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `jilo_agent_type` ( CREATE TABLE `jilo_agent_types` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(255), `description` varchar(255),
`endpoint` varchar(255), `endpoint` varchar(255),
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
INSERT INTO `jilo_agent_type` (`id`, `description`, `endpoint`) VALUES INSERT INTO `jilo_agent_types` (`id`, `description`, `endpoint`) VALUES
(1,'jvb','/jvb'), (1,'jvb','/jvb'),
(2,'jicofo','/jicofo'), (2,'jicofo','/jicofo'),
(3,'prosody','/prosody'), (3,'prosody','/prosody'),
@ -209,7 +209,7 @@ INSERT INTO `jilo_agent_type` (`id`, `description`, `endpoint`) VALUES
(5,'jibri','/jibri'); (5,'jibri','/jibri');
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `platform` ( CREATE TABLE `platforms` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL, `name` varchar(255) NOT NULL,
`jitsi_url` varchar(255) NOT NULL, `jitsi_url` varchar(255) NOT NULL,
@ -221,17 +221,17 @@ INSERT INTO `platforms` (`id`, `name`, `jitsi_url`, `jilo_database`) VALUES
(1,'example.com','https://meet.example.com','../../jilo/jilo.db'); (1,'example.com','https://meet.example.com','../../jilo/jilo.db');
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `host` ( CREATE TABLE `hosts` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(255) NOT NULL, `address` varchar(255) NOT NULL,
`platform_id` int(11) NOT NULL, `platform_id` int(11) NOT NULL,
`name` varchar(255), `name` varchar(255),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `host_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platform` (`id`) CONSTRAINT `hosts_ibfk_1` FOREIGN KEY (`platform_id`) REFERENCES `platforms` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `jilo_agent` ( CREATE TABLE `jilo_agents` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`host_id` int(11) NOT NULL, `host_id` int(11) NOT NULL,
`agent_type_id` int(11) NOT NULL, `agent_type_id` int(11) NOT NULL,
@ -239,12 +239,12 @@ CREATE TABLE `jilo_agent` (
`secret_key` varchar(255), `secret_key` varchar(255),
`check_period` int(11) DEFAULT 0, `check_period` int(11) DEFAULT 0,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `jilo_agent_ibfk_1` FOREIGN KEY (`agent_type_id`) REFERENCES `jilo_agent_type` (`id`), CONSTRAINT `jilo_agents_ibfk_1` FOREIGN KEY (`agent_type_id`) REFERENCES `jilo_agent_types` (`id`),
CONSTRAINT `jilo_agent_ibfk_2` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`) CONSTRAINT `jilo_agents_ibfk_2` FOREIGN KEY (`host_id`) REFERENCES `hosts` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- -------------------------------------------------------- -- --------------------------------------------------------
CREATE TABLE `jilo_agent_check` ( CREATE TABLE jilo_agent_checks (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`agent_id` int(11), `agent_id` int(11),
`timestamp` datetime DEFAULT current_timestamp(), `timestamp` datetime DEFAULT current_timestamp(),
@ -252,8 +252,9 @@ CREATE TABLE `jilo_agent_check` (
`response_time_ms` int(11), `response_time_ms` int(11),
`response_content` varchar(255), `response_content` varchar(255),
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
CONSTRAINT `jilo_agent_check_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `jilo_agent` (`id`) CONSTRAINT `jilo_agent_checks_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `jilo_agents` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
COMMIT; COMMIT;

View File

@ -32,7 +32,7 @@ class Log {
*/ */
public function insertLog($userId, $message, $scope = 'user') { public function insertLog($userId, $message, $scope = 'user') {
try { try {
$sql = 'INSERT INTO log $sql = 'INSERT INTO logs
(user_id, scope, message) (user_id, scope, message)
VALUES VALUES
(:user_id, :scope, :message)'; (:user_id, :scope, :message)';
@ -68,8 +68,8 @@ class Log {
// Base query with user join // Base query with user join
$base_sql = 'SELECT l.*, u.username $base_sql = 'SELECT l.*, u.username
FROM log l FROM logs l
LEFT JOIN user u ON l.user_id = u.id'; LEFT JOIN users u ON l.user_id = u.id';
// Add scope condition // Add scope condition
if ($scope === 'user') { if ($scope === 'user') {

View File

@ -21,13 +21,13 @@ require_once dirname(__FILE__, 4) . '/app/helpers/security.php';
if ($config['registration_enabled'] == true) { if ($config['registration_enabled'] == true) {
try { try {
global $db, $logObject, $userObject; global $dbWeb, $logObject, $userObject;
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) { if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
// Apply rate limiting // Apply rate limiting
require_once dirname(__FILE__, 4) . '/app/includes/rate_limit_middleware.php'; require_once dirname(__FILE__, 4) . '/app/includes/rate_limit_middleware.php';
checkRateLimit($db, 'register'); checkRateLimit($dbWeb, 'register');
$security = SecurityHelper::getInstance(); $security = SecurityHelper::getInstance();
@ -67,7 +67,7 @@ if ($config['registration_enabled'] == true) {
$password = $formData['password']; $password = $formData['password'];
// registering // registering
$register = new Register($db); $register = new Register($dbWeb);
$result = $register->register($username, $password); $result = $register->register($username, $password);
// redirect to login // redirect to login

View File

@ -49,9 +49,9 @@ class Register {
// hash the password, don't store it plain // hash the password, don't store it plain
$hashedPassword = password_hash($password, PASSWORD_DEFAULT); $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// insert into user table // insert into users table
$sql = 'INSERT $sql = 'INSERT
INTO user (username, password) INTO users (username, password)
VALUES (:username, :password)'; VALUES (:username, :password)';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindValue(':username', $username); $query->bindValue(':username', $username);
@ -64,9 +64,9 @@ class Register {
return false; return false;
} }
// insert the last user id into user_meta table // insert the last user id into users_meta table
$sql2 = 'INSERT $sql2 = 'INSERT
INTO user_meta (user_id) INTO users_meta (user_id)
VALUES (:user_id)'; VALUES (:user_id)';
$query2 = $this->db->prepare($sql2); $query2 = $this->db->prepare($sql2);
$query2->bindValue(':user_id', $this->db->lastInsertId()); $query2->bindValue(':user_id', $this->db->lastInsertId());

View File

@ -80,13 +80,30 @@ error_reporting(E_ALL);
// edit accordingly, add 'pages/PAGE.php' // edit accordingly, add 'pages/PAGE.php'
$allowed_urls = [ $allowed_urls = [
'dashboard', 'dashboard',
'conferences','participants','components',
'graphs','latest','livejs','agents', 'conferences',
'profile','credentials','config','security', 'participants',
'components',
'graphs',
'latest',
'livejs',
'agents',
'config',
'profile',
'credentials',
'settings', 'settings',
'security',
'status', 'status',
'help', 'help',
'login','logout',
'login',
'logout',
'about', 'about',
]; ];
@ -120,10 +137,10 @@ require_once __DIR__ . '/../app/core/Router.php';
use App\Core\Router; use App\Core\Router;
$currentUser = Router::checkAuth($config, $app_root, $public_pages, $page); $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'; require_once __DIR__ . '/../app/core/DatabaseConnector.php';
use App\Core\DatabaseConnector; use App\Core\DatabaseConnector;
$db = DatabaseConnector::connect($config); $dbWeb = DatabaseConnector::connect($config);
// Logging: default to NullLogger, plugin can override // Logging: default to NullLogger, plugin can override
require_once __DIR__ . '/../app/core/NullLogger.php'; require_once __DIR__ . '/../app/core/NullLogger.php';
@ -134,7 +151,7 @@ require_once __DIR__ . '/../app/helpers/ip_helper.php';
$user_IP = ''; $user_IP = '';
// Plugin: initialize logging system plugin if available // Plugin: initialize logging system plugin if available
do_hook('logger.system_init', ['db' => $db]); do_hook('logger.system_init', ['db' => $dbWeb]);
// Override defaults if plugin provided real logger // Override defaults if plugin provided real logger
if (isset($GLOBALS['logObject'])) { if (isset($GLOBALS['logObject'])) {
@ -157,7 +174,7 @@ require '../app/classes/ratelimiter.php';
// get platforms details // get platforms details
require '../app/classes/platform.php'; require '../app/classes/platform.php';
$platformObject = new Platform($db); $platformObject = new Platform($dbWeb);
$platformsAll = $platformObject->getPlatformDetails(); $platformsAll = $platformObject->getPlatformDetails();
// by default we connect ot the first configured platform // by default we connect ot the first configured platform
@ -170,7 +187,7 @@ $platformDetails = $platformObject->getPlatformDetails($platform_id);
// init user functions // init user functions
require '../app/classes/user.php'; require '../app/classes/user.php';
include '../app/helpers/profile.php'; include '../app/helpers/profile.php';
$userObject = new User($db); $userObject = new User($dbWeb);
// logout is a special case, as we can't use session vars for notices // logout is a special case, as we can't use session vars for notices
if ($page == 'logout') { if ($page == 'logout') {
@ -208,7 +225,7 @@ if ($page == 'logout') {
// check if the Jilo Server is running // check if the Jilo Server is running
require '../app/classes/server.php'; require '../app/classes/server.php';
$serverObject = new Server($db); $serverObject = new Server($dbWeb);
$server_host = '127.0.0.1'; $server_host = '127.0.0.1';
$server_port = '8080'; $server_port = '8080';
@ -238,9 +255,9 @@ if ($page == 'logout') {
// all normal pages // all normal pages
if (isset($plugin_controllers[$page])) { if (isset($plugin_controllers[$page])) {
include $plugin_controllers[$page]; include $plugin_controllers[$page];
} else { } else {
include "../app/pages/{$page}.php"; include "../app/pages/{$page}.php";
} }
} else { } else {
// the page is not in allowed urls, loading "not found" page // the page is not in allowed urls, loading "not found" page
include '../app/templates/error-notfound.php'; include '../app/templates/error-notfound.php';

View File

@ -16,235 +16,137 @@ class RateLimitMiddlewareTest extends TestCase
{ {
parent::setUp(); parent::setUp();
// Set global IP for rate limiting
global $user_IP;
$user_IP = '8.8.8.8';
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
// Set up test database // Set up test database
$this->db = new Database([ $this->db = new Database([
'type' => 'mariadb', 'type' => 'sqlite',
'host' => $host, 'dbFile' => ':memory:'
'port' => '3306',
'dbname' => 'totalmeet_test',
'user' => 'test_totalmeet',
'password' => $password
]); ]);
// Create rate limiter instance // Create rate limiter table
$this->db->getConnection()->exec("CREATE TABLE pages_rate_limits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL,
endpoint TEXT NOT NULL,
request_time DATETIME DEFAULT CURRENT_TIMESTAMP
)");
// Create ip_whitelist table
$this->db->getConnection()->exec("CREATE TABLE ip_whitelist (
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
)");
// Create ip_blacklist table
$this->db->getConnection()->exec("CREATE TABLE ip_blacklist (
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
)");
$this->rateLimiter = new RateLimiter($this->db); $this->rateLimiter = new RateLimiter($this->db);
// Drop tables if they exist // Mock $_SERVER variables
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_auth"); $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_rate_page"); $_SERVER['REQUEST_URI'] = '/login';
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_ip_blacklist"); $_SERVER['REQUEST_METHOD'] = 'POST';
$this->db->getConnection()->exec("DROP TABLE IF EXISTS security_ip_whitelist");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS log");
// Create required tables with correct names from RateLimiter class // Define testing constant
$this->db->getConnection()->exec(" if (!defined('TESTING')) {
CREATE TABLE IF NOT EXISTS security_rate_auth ( define('TESTING', true);
id INT 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->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_rate_page (
id INT 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->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_ip_blacklist (
id INT 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->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_ip_whitelist (
id INT 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)
)
");
// Create log table
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS log (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
scope VARCHAR(50) NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
// Mock $_SERVER['REMOTE_ADDR'] with a non-whitelisted IP
$_SERVER['REMOTE_ADDR'] = '8.8.8.8';
// Define PHPUNIT_RUNNING constant
if (!defined('PHPUNIT_RUNNING')) {
define('PHPUNIT_RUNNING', true);
} }
} }
protected function tearDown(): void protected function tearDown(): void
{ {
// Clean up all rate limit records // Clean up rate limit records
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page"); $this->db->getConnection()->exec('DELETE FROM pages_rate_limits');
$this->db->getConnection()->exec("TRUNCATE TABLE security_ip_blacklist");
$this->db->getConnection()->exec("TRUNCATE TABLE security_ip_whitelist");
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_auth");
$this->db->getConnection()->exec("TRUNCATE TABLE log");
parent::tearDown(); parent::tearDown();
} }
public function testRateLimitMiddleware() public function testRateLimitMiddleware()
{ {
// Clean any existing rate limit records // Test multiple requests
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page"); for ($i = 1; $i <= 5; $i++) {
// Make 60 requests to reach the limit
for ($i = 0; $i < 60; $i++) {
$result = checkRateLimit($this->db, '/login'); $result = checkRateLimit($this->db, '/login');
$this->assertTrue($result, "Request $i should be allowed");
// Verify request was recorded if ($i <= 5) {
$stmt = $this->db->getConnection()->prepare(" // First 5 requests should pass
SELECT COUNT(*) as count $this->assertTrue($result);
FROM security_rate_page } else {
WHERE ip_address = ? // 6th and subsequent requests should be blocked
AND endpoint = ? $this->assertFalse($result);
AND request_time >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) }
");
$stmt->execute(['8.8.8.8', '/login']);
$count = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
$this->assertEquals($i + 1, $count, "Expected " . ($i + 1) . " requests to be recorded, got {$count}");
} }
// The 61st request should be blocked
$result = checkRateLimit($this->db, '/login');
$this->assertFalse($result, "Request should be blocked after 60 requests");
} }
public function testRateLimitBypass() public function testRateLimitBypass()
{ {
// Clean any existing rate limit records and lists // Test AJAX request bypass
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page"); $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
$this->db->getConnection()->exec("TRUNCATE TABLE security_ip_whitelist"); $result = checkRateLimit($this->db, '/login');
$this->db->getConnection()->exec("TRUNCATE TABLE security_ip_blacklist"); $this->assertTrue($result);
// Add IP to whitelist and verify it was added
$stmt = $this->db->getConnection()->prepare("INSERT INTO security_ip_whitelist (ip_address, is_network, description, created_by) VALUES (?, 0, ?, 'PHPUnit')");
$stmt->execute(['8.8.8.8', 'Test whitelist']);
// Verify IP is in whitelist
$stmt = $this->db->getConnection()->prepare("SELECT COUNT(*) as count FROM security_ip_whitelist WHERE ip_address = ?");
$stmt->execute(['8.8.8.8']);
$count = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
$this->assertEquals(1, $count, "IP should be in whitelist");
// Should be able to make more requests than limit
for ($i = 0; $i < 100; $i++) {
$result = checkRateLimit($this->db, '/login');
$this->assertTrue($result, "Request $i should be allowed for whitelisted IP");
}
} }
public function testRateLimitReset() public function testRateLimitReset()
{ {
// Clean any existing rate limit records // Use up the rate limit
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page"); for ($i = 0; $i < 5; $i++) {
checkRateLimit($this->db, '/login');
// Make some requests
for ($i = 0; $i < 15; $i++) {
$result = checkRateLimit($this->db, '/login');
} }
// Manually expire old requests // Wait for rate limit to reset (use a short window for testing)
$this->db->getConnection()->exec(" sleep(2);
DELETE FROM security_rate_page
WHERE request_time < DATE_SUB(NOW(), INTERVAL 1 MINUTE)
");
// Should be able to make requests again // Should be able to make request again
$result = checkRateLimit($this->db, '/login'); $result = checkRateLimit($this->db, '/login');
$this->assertTrue($result); $this->assertTrue($result);
} }
public function testDifferentEndpoints() public function testDifferentEndpoints()
{ {
// Clean any existing rate limit records // Use up rate limit for login
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page");
// Make requests to login endpoint (default limit: 60)
for ($i = 0; $i < 30; $i++) {
$result = checkRateLimit($this->db, '/login');
$this->assertTrue($result, "Request $i to /login should be allowed");
}
// Clean up between endpoint tests
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page");
// Make requests to register endpoint (limit: 5)
for ($i = 0; $i < 5; $i++) { for ($i = 0; $i < 5; $i++) {
$result = checkRateLimit($this->db, '/register'); checkRateLimit($this->db, '/login');
$this->assertTrue($result, "Request $i to /register should be allowed");
} }
// The 6th request to register should be blocked // Should still be able to access different endpoint
$result = checkRateLimit($this->db, '/register'); $result = checkRateLimit($this->db, '/dashboard');
$this->assertFalse($result, "Request should be blocked after 5 requests to /register"); $this->assertTrue($result);
} }
public function testDifferentIpAddresses() public function testDifferentIpAddresses()
{ {
// Make requests from first IP // Use up rate limit for first IP
for ($i = 0; $i < 30; $i++) { $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$result = checkRateLimit($this->db, '/login'); for ($i = 0; $i < 5; $i++) {
$this->assertTrue($result); checkRateLimit($this->db, '/login');
} }
// Change IP // Different IP should not be affected
$_SERVER['REMOTE_ADDR'] = '8.8.4.4'; $_SERVER['REMOTE_ADDR'] = '127.0.0.2';
$result = checkRateLimit($this->db, '/login');
// Should be able to make requests from different IP $this->assertTrue($result);
for ($i = 0; $i < 30; $i++) {
$result = checkRateLimit($this->db, '/login');
$this->assertTrue($result);
}
} }
public function testWhitelistedIp() public function testWhitelistedIp()
{ {
// Add IP to whitelist // Add IP to whitelist
$this->rateLimiter->addToWhitelist('8.8.8.8', false, 'Test whitelist', 'PHPUnit'); $this->db->execute(
'INSERT INTO ip_whitelist (ip_address, description, created_by) VALUES (?, ?, ?)',
['127.0.0.1', 'Test whitelist', 'PHPUnit']
);
// Should be able to make more requests than limit // Should be able to make more requests than limit
for ($i = 0; $i < 50; $i++) { for ($i = 0; $i < 10; $i++) {
$result = checkRateLimit($this->db, '/login'); $result = checkRateLimit($this->db, '/login');
$this->assertTrue($result); $this->assertTrue($result);
} }
@ -252,39 +154,30 @@ class RateLimitMiddlewareTest extends TestCase
public function testBlacklistedIp() public function testBlacklistedIp()
{ {
// Add IP to blacklist and verify it was added // Add IP to blacklist
$this->db->getConnection()->exec("INSERT INTO security_ip_blacklist (ip_address, is_network, reason, created_by) VALUES ('8.8.8.8', 0, 'Test blacklist', 'system')"); $this->db->execute(
'INSERT INTO ip_blacklist (ip_address, reason, created_by) VALUES (?, ?, ?)',
['127.0.0.1', 'Test blacklist', 'PHPUnit']
);
// Request should be blocked // Should be blocked immediately
$result = checkRateLimit($this->db, '/login'); $result = checkRateLimit($this->db, '/login');
$this->assertFalse($result, "Blacklisted IP should be blocked"); $this->assertFalse($result);
} }
public function testRateLimitPersistence() public function testRateLimitPersistence()
{ {
// Clean any existing rate limit records // Use up some of the rate limit
$this->db->getConnection()->exec("TRUNCATE TABLE security_rate_page"); for ($i = 0; $i < 2; $i++) {
checkRateLimit($this->db, '/login');
// Make 60 requests to reach the limit
for ($i = 0; $i < 60; $i++) {
$result = checkRateLimit($this->db, '/login');
$this->assertTrue($result, "Request $i should be allowed");
// Verify request was recorded
$stmt = $this->db->getConnection()->prepare("
SELECT COUNT(*) as count
FROM security_rate_page
WHERE ip_address = ?
AND endpoint = ?
AND request_time >= DATE_SUB(NOW(), INTERVAL 1 MINUTE)
");
$stmt->execute(['8.8.8.8', '/login']);
$count = $stmt->fetch(PDO::FETCH_ASSOC)['count'];
$this->assertEquals($i + 1, $count, "Expected " . ($i + 1) . " requests to be recorded, got {$count}");
} }
// The 61st request should be blocked // Destroy and restart session
//session_destroy();
//session_start();
// Should still count previous requests
$result = checkRateLimit($this->db, '/login'); $result = checkRateLimit($this->db, '/login');
$this->assertFalse($result, "Request should be blocked after 60 requests"); $this->assertTrue($result);
} }
} }

View File

@ -24,9 +24,9 @@ class AgentTest extends TestCase
'dbFile' => ':memory:' 'dbFile' => ':memory:'
]); ]);
// Create jilo_agent table // Create jilo_agents table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE jilo_agent ( CREATE TABLE jilo_agents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
host_id INTEGER NOT NULL, host_id INTEGER NOT NULL,
agent_type_id INTEGER NOT NULL, agent_type_id INTEGER NOT NULL,
@ -38,18 +38,18 @@ class AgentTest extends TestCase
) )
"); ");
// Create jilo_agent_type table // Create jilo_agent_types table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE jilo_agent_type ( CREATE TABLE jilo_agent_types (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL, description TEXT NOT NULL,
endpoint TEXT NOT NULL endpoint TEXT NOT NULL
) )
"); ");
// Create host table // Create hosts table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE host ( CREATE TABLE hosts (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
platform_id INTEGER NOT NULL, platform_id INTEGER NOT NULL,
name TEXT NOT NULL name TEXT NOT NULL
@ -58,12 +58,12 @@ class AgentTest extends TestCase
// Insert test host // Insert test host
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
INSERT INTO host (id, platform_id, name) VALUES (1, 1, 'Test Host') INSERT INTO hosts (id, platform_id, name) VALUES (1, 1, 'Test Host')
"); ");
// Insert test agent type // Insert test agent type
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
INSERT INTO jilo_agent_type (id, description, endpoint) INSERT INTO jilo_agent_types (id, description, endpoint)
VALUES (1, 'Test Agent Type', '/api/test') VALUES (1, 'Test Agent Type', '/api/test')
"); ");
@ -85,7 +85,7 @@ class AgentTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify agent was created // Verify agent was created
$stmt = $this->db->getConnection()->prepare('SELECT * FROM jilo_agent WHERE host_id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT * FROM jilo_agents WHERE host_id = ?');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$agent = $stmt->fetch(PDO::FETCH_ASSOC); $agent = $stmt->fetch(PDO::FETCH_ASSOC);
@ -131,7 +131,7 @@ class AgentTest extends TestCase
$this->agent->addAgent($hostId, $data); $this->agent->addAgent($hostId, $data);
// Get agent ID // Get agent ID
$stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agent WHERE host_id = ? LIMIT 1'); $stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agents WHERE host_id = ? LIMIT 1');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$agentId = $stmt->fetch(PDO::FETCH_COLUMN); $agentId = $stmt->fetch(PDO::FETCH_COLUMN);
@ -148,7 +148,7 @@ class AgentTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify update // Verify update
$stmt = $this->db->getConnection()->prepare('SELECT * FROM jilo_agent WHERE id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT * FROM jilo_agents WHERE id = ?');
$stmt->execute([$agentId]); $stmt->execute([$agentId]);
$agent = $stmt->fetch(PDO::FETCH_ASSOC); $agent = $stmt->fetch(PDO::FETCH_ASSOC);
@ -171,7 +171,7 @@ class AgentTest extends TestCase
$this->agent->addAgent($hostId, $data); $this->agent->addAgent($hostId, $data);
// Get agent ID // Get agent ID
$stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agent WHERE host_id = ? LIMIT 1'); $stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agents WHERE host_id = ? LIMIT 1');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$agentId = $stmt->fetch(PDO::FETCH_COLUMN); $agentId = $stmt->fetch(PDO::FETCH_COLUMN);
@ -180,7 +180,7 @@ class AgentTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify deletion // Verify deletion
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM jilo_agent WHERE id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM jilo_agents WHERE id = ?');
$stmt->execute([$agentId]); $stmt->execute([$agentId]);
$count = $stmt->fetch(PDO::FETCH_COLUMN); $count = $stmt->fetch(PDO::FETCH_COLUMN);
@ -201,7 +201,7 @@ class AgentTest extends TestCase
$this->agent->addAgent($hostId, $data); $this->agent->addAgent($hostId, $data);
// Get agent ID // Get agent ID
$stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agent WHERE host_id = ? LIMIT 1'); $stmt = $this->db->getConnection()->prepare('SELECT id FROM jilo_agents WHERE host_id = ? LIMIT 1');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$agentId = $stmt->fetch(PDO::FETCH_COLUMN); $agentId = $stmt->fetch(PDO::FETCH_COLUMN);

View File

@ -23,9 +23,9 @@ class HostTest extends TestCase
'dbFile' => ':memory:' 'dbFile' => ':memory:'
]); ]);
// Create host table // Create hosts table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE host ( CREATE TABLE hosts (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
platform_id INTEGER NOT NULL, platform_id INTEGER NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
@ -33,9 +33,9 @@ class HostTest extends TestCase
) )
"); ");
// Create jilo_agent table for relationship testing // Create jilo_agents table for relationship testing
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE jilo_agent ( CREATE TABLE jilo_agents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
host_id INTEGER NOT NULL, host_id INTEGER NOT NULL,
agent_type_id INTEGER NOT NULL, agent_type_id INTEGER NOT NULL,
@ -60,7 +60,7 @@ class HostTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify host was created // Verify host was created
$stmt = $this->db->getConnection()->prepare('SELECT * FROM host WHERE platform_id = ? AND name = ?'); $stmt = $this->db->getConnection()->prepare('SELECT * FROM hosts WHERE platform_id = ? AND name = ?');
$stmt->execute([$data['platform_id'], $data['name']]); $stmt->execute([$data['platform_id'], $data['name']]);
$host = $stmt->fetch(\PDO::FETCH_ASSOC); $host = $stmt->fetch(\PDO::FETCH_ASSOC);
@ -107,7 +107,7 @@ class HostTest extends TestCase
$this->host->addHost($data); $this->host->addHost($data);
// Get host ID // Get host ID
$stmt = $this->db->getConnection()->prepare('SELECT id FROM host WHERE platform_id = ? AND name = ?'); $stmt = $this->db->getConnection()->prepare('SELECT id FROM hosts WHERE platform_id = ? AND name = ?');
$stmt->execute([$data['platform_id'], $data['name']]); $stmt->execute([$data['platform_id'], $data['name']]);
$hostId = $stmt->fetch(\PDO::FETCH_COLUMN); $hostId = $stmt->fetch(\PDO::FETCH_COLUMN);
@ -122,7 +122,7 @@ class HostTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify update // Verify update
$stmt = $this->db->getConnection()->prepare('SELECT * FROM host WHERE id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT * FROM hosts WHERE id = ?');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$host = $stmt->fetch(\PDO::FETCH_ASSOC); $host = $stmt->fetch(\PDO::FETCH_ASSOC);
@ -141,13 +141,13 @@ class HostTest extends TestCase
$this->host->addHost($data); $this->host->addHost($data);
// Get host ID // Get host ID
$stmt = $this->db->getConnection()->prepare('SELECT id FROM host WHERE platform_id = ? AND name = ?'); $stmt = $this->db->getConnection()->prepare('SELECT id FROM hosts WHERE platform_id = ? AND name = ?');
$stmt->execute([$data['platform_id'], $data['name']]); $stmt->execute([$data['platform_id'], $data['name']]);
$hostId = $stmt->fetch(\PDO::FETCH_COLUMN); $hostId = $stmt->fetch(\PDO::FETCH_COLUMN);
// Add test agent to the host // Add test agent to the host
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
INSERT INTO jilo_agent (host_id, agent_type_id, url, secret_key) INSERT INTO jilo_agents (host_id, agent_type_id, url, secret_key)
VALUES ($hostId, 1, 'http://test:8080', 'secret') VALUES ($hostId, 1, 'http://test:8080', 'secret')
"); ");
@ -156,13 +156,13 @@ class HostTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify host deletion // Verify host deletion
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM host WHERE id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM hosts WHERE id = ?');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$hostCount = $stmt->fetch(\PDO::FETCH_COLUMN); $hostCount = $stmt->fetch(\PDO::FETCH_COLUMN);
$this->assertEquals(0, $hostCount); $this->assertEquals(0, $hostCount);
// Verify agent deletion // Verify agent deletion
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM jilo_agent WHERE host_id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) FROM jilo_agents WHERE host_id = ?');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$agentCount = $stmt->fetch(\PDO::FETCH_COLUMN); $agentCount = $stmt->fetch(\PDO::FETCH_COLUMN);
$this->assertEquals(0, $agentCount); $this->assertEquals(0, $agentCount);

View File

@ -1,261 +1,138 @@
<?php <?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/log.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/**
* TestLogger class for testing log functionality
* This is a simplified implementation that mimics the plugin's Log class
* but with a different name to avoid conflicts
*/
class TestLogger {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
public function insertLog($userId, $message, $scope = 'user') {
try {
$sql = 'INSERT INTO log
(user_id, scope, message)
VALUES
(:user_id, :scope, :message)';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $userId,
':scope' => $scope,
':message' => $message,
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
public function readLog($userId, $scope, $offset = 0, $items_per_page = '', $filters = []) {
$params = [];
$where_clauses = [];
// Base query with user join
$base_sql = 'SELECT l.*, u.username
FROM log l
LEFT JOIN user u ON l.user_id = u.id';
// Add scope condition
if ($scope === 'user') {
$where_clauses[] = 'l.user_id = :user_id';
$params[':user_id'] = $userId;
}
// Add time range filters if specified
if (!empty($filters['from_time'])) {
$where_clauses[] = 'l.time >= :from_time';
$params[':from_time'] = $filters['from_time'] . ' 00:00:00';
}
if (!empty($filters['until_time'])) {
$where_clauses[] = 'l.time <= :until_time';
$params[':until_time'] = $filters['until_time'] . ' 23:59:59';
}
// Add message search if specified
if (!empty($filters['message'])) {
$where_clauses[] = 'l.message LIKE :message';
$params[':message'] = '%' . $filters['message'] . '%';
}
// Add user ID search if specified
if (!empty($filters['id'])) {
$where_clauses[] = 'l.user_id = :search_user_id';
$params[':search_user_id'] = $filters['id'];
}
// Combine WHERE clauses
$sql = $base_sql;
if (!empty($where_clauses)) {
$sql .= ' WHERE ' . implode(' AND ', $where_clauses);
}
// Add ordering
$sql .= ' ORDER BY l.time DESC';
// Add pagination
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$query = $this->db->prepare($sql);
$query->execute($params);
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
class LogTest extends TestCase class LogTest extends TestCase
{ {
private $db; private $db;
private $log; private $log;
private $testUserId;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
// Prepare DB for Github CI
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
// Set up test database // Set up test database
$this->db = new Database([ $this->db = new Database([
'type' => 'mariadb', 'type' => 'sqlite',
'host' => $host, 'dbFile' => ':memory:'
'port' => '3306',
'dbname' => 'totalmeet_test',
'user' => 'test_totalmeet',
'password' => $password
]); ]);
// Create user table // Create users table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user ( CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY, id INTEGER PRIMARY KEY,
username VARCHAR(255) NOT NULL, username TEXT NOT NULL
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) )
"); ");
// Create log table with the expected schema from Log class // Create test user
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS log ( INSERT INTO users (id, username) VALUES (1, 'testuser'), (2, 'testuser2')
id INT AUTO_INCREMENT PRIMARY KEY, ");
user_id INT,
scope VARCHAR(50) NOT NULL DEFAULT 'user', // Create logs table
$this->db->getConnection()->exec("
CREATE TABLE logs (
id INTEGER PRIMARY KEY,
user_id INTEGER,
scope TEXT NOT NULL,
message TEXT NOT NULL, message TEXT NOT NULL,
time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) FOREIGN KEY (user_id) REFERENCES users(id)
) )
"); ");
// Create test users with all required fields $this->log = new Log($this->db);
$this->db->getConnection()->exec("
INSERT INTO user (username, password, email)
VALUES
('testuser', 'password123', 'testuser@example.com'),
('testuser2', 'password123', 'testuser2@example.com')
");
// Store test user ID for later use
$stmt = $this->db->getConnection()->query("SELECT id FROM user WHERE username = 'testuser' LIMIT 1");
$user = $stmt->fetch(PDO::FETCH_ASSOC);
$this->testUserId = $user['id'];
// Create a TestLogger instance that will be used by the app's Log wrapper
$this->log = new TestLogger($this->db);
}
protected function tearDown(): void
{
// Drop tables in correct order (respect foreign key constraints)
$this->db->getConnection()->exec("DROP TABLE IF EXISTS log");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user");
parent::tearDown();
} }
public function testInsertLog() public function testInsertLog()
{ {
$result = $this->log->insertLog($this->testUserId, 'Test message', 'test'); $result = $this->log->insertLog(1, 'Test message', 'test');
$this->assertTrue($result); $this->assertTrue($result);
// Verify the log was inserted $stmt = $this->db->getConnection()->prepare("SELECT * FROM logs WHERE scope = ?");
$stmt = $this->db->getConnection()->query("SELECT * FROM log WHERE user_id = {$this->testUserId} LIMIT 1"); $stmt->execute(['test']);
$log = $stmt->fetch(PDO::FETCH_ASSOC); $log = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(1, $log['user_id']);
$this->assertEquals('Test message', $log['message']); $this->assertEquals('Test message', $log['message']);
$this->assertEquals('test', $log['scope']); $this->assertEquals('test', $log['scope']);
} }
public function testReadLog() public function testReadLog()
{ {
// Insert some test logs with a delay to ensure order // Insert test logs
$this->log->insertLog($this->testUserId, 'Test message 1', 'user'); $this->log->insertLog(1, 'Test message 1', 'user');
sleep(1); // Ensure different timestamps $this->log->insertLog(1, 'Test message 2', 'user');
$this->log->insertLog($this->testUserId, 'Test message 2', 'user');
$logs = $this->log->readLog($this->testUserId, 'user'); $logs = $this->log->readLog(1, 'user');
$this->assertCount(2, $logs); $this->assertCount(2, $logs);
$this->assertEquals('Test message 2', $logs[0]['message']); // Most recent first (by time) $this->assertEquals('Test message 1', $logs[0]['message']);
$this->assertEquals('Test message 2', $logs[1]['message']);
} }
public function testReadLogWithTimeFilter() public function testReadLogWithTimeFilter()
{ {
// First message from yesterday // Insert test logs with different times
$yesterday = date('Y-m-d', strtotime('-1 day')); $this->log->insertLog(1, 'Old message', 'user');
$stmt = $this->db->getConnection()->prepare(" sleep(1); // Ensure different timestamps
INSERT INTO log (user_id, scope, message, time) $this->log->insertLog(1, 'New message', 'user');
VALUES (?, ?, ?, ?)
");
$stmt->execute([$this->testUserId, 'user', 'Test message 1', $yesterday . ' 12:00:00']);
// Second message from today $now = date('Y-m-d H:i:s');
$today = date('Y-m-d'); $oneHourAgo = date('Y-m-d H:i:s', strtotime('-1 hour'));
$stmt->execute([$this->testUserId, 'user', 'Test message 2', $today . ' 12:00:00']);
// Should get only today's messages $logs = $this->log->readLog(1, 'user', 0, '', [
$logs = $this->log->readLog($this->testUserId, 'user', 0, '', [ 'from_time' => $oneHourAgo,
'from_time' => $today 'until_time' => $now
]); ]);
$this->assertCount(1, $logs);
$this->assertEquals('Test message 2', $logs[0]['message']); // Most recent first $this->assertCount(2, $logs);
} }
public function testReadLogWithPagination() public function testReadLogWithPagination()
{ {
// Insert multiple test logs with delays to ensure order // Insert test logs
for ($i = 1; $i <= 5; $i++) { $this->log->insertLog(1, 'Message 1', 'user');
$this->log->insertLog($this->testUserId, "Test message $i", 'user'); $this->log->insertLog(1, 'Message 2', 'user');
sleep(1); // Ensure different timestamps $this->log->insertLog(1, 'Message 3', 'user');
}
// Get all logs to verify total // Test with limit
$allLogs = $this->log->readLog($this->testUserId, 'user'); $logs = $this->log->readLog(1, 'user', 0, 2);
$this->assertCount(5, $allLogs);
// Get first page (offset 0, limit 2)
$logs = $this->log->readLog($this->testUserId, 'user', 0, 2);
$this->assertCount(2, $logs); $this->assertCount(2, $logs);
$this->assertEquals('Test message 5', $logs[0]['message']); // Most recent first
$this->assertEquals('Test message 4', $logs[1]['message']);
// Get second page (offset 2, limit 2) // Test with offset
$logs = $this->log->readLog($this->testUserId, 'user', 2, 2); $logs = $this->log->readLog(1, 'user', 2, 2);
$this->assertCount(2, $logs); $this->assertCount(1, $logs);
$this->assertEquals('Test message 3', $logs[0]['message']);
$this->assertEquals('Test message 2', $logs[1]['message']);
} }
public function testReadLogWithMessageFilter() public function testReadLogWithMessageFilter()
{ {
// Insert test logs with different messages and delays // Insert test logs
$this->log->insertLog($this->testUserId, 'Test message ABC', 'user'); $this->log->insertLog(1, 'Test message', 'user');
sleep(1); // Ensure different timestamps $this->log->insertLog(1, 'Another message', 'user');
$this->log->insertLog($this->testUserId, 'Test message XYZ', 'user');
sleep(1); // Ensure different timestamps
$this->log->insertLog($this->testUserId, 'Different message', 'user');
// Filter by message content $logs = $this->log->readLog(1, 'user', 0, '', [
$logs = $this->log->readLog($this->testUserId, 'user', 0, '', ['message' => 'Test message']); 'message' => 'Test'
$this->assertCount(2, $logs); ]);
// Verify filtered results $this->assertCount(1, $logs);
foreach ($logs as $log) { $this->assertEquals('Test message', $logs[0]['message']);
$this->assertStringContainsString('Test message', $log['message']); }
}
public function testReadLogWithUserFilter()
{
// Insert test logs for different users
$this->log->insertLog(1, 'User 1 message', 'user');
$this->log->insertLog(2, 'User 2 message', 'user');
$logs = $this->log->readLog(1, 'user', 0, '', [
'id' => 1
]);
$this->assertCount(1, $logs);
$this->assertEquals('User 1 message', $logs[0]['message']);
} }
} }

View File

@ -20,26 +20,26 @@ class PlatformTest extends TestCase
'dbFile' => ':memory:' 'dbFile' => ':memory:'
]); ]);
// Create host table // Create hosts table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE host ( CREATE TABLE hosts (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
platform_id INTEGER NOT NULL, platform_id INTEGER NOT NULL,
name TEXT NOT NULL name TEXT NOT NULL
) )
"); ");
// Create jilo_agent table // Create jilo_agents table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE jilo_agent ( CREATE TABLE jilo_agents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
host_id INTEGER NOT NULL host_id INTEGER NOT NULL
) )
"); ");
// Create platform table // Create platforms table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE platform ( CREATE TABLE platforms (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
jitsi_url TEXT NOT NULL, jitsi_url TEXT NOT NULL,
@ -64,7 +64,7 @@ class PlatformTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify platform was created // Verify platform was created
$stmt = $this->db->getConnection()->prepare('SELECT * FROM platform WHERE name = ?'); $stmt = $this->db->getConnection()->prepare('SELECT * FROM platforms WHERE name = ?');
$stmt->execute([$data['name']]); $stmt->execute([$data['name']]);
$platform = $stmt->fetch(PDO::FETCH_ASSOC); $platform = $stmt->fetch(PDO::FETCH_ASSOC);
@ -77,7 +77,7 @@ class PlatformTest extends TestCase
public function testGetPlatformDetails() public function testGetPlatformDetails()
{ {
// Create test platform // Create test platform
$stmt = $this->db->getConnection()->prepare('INSERT INTO platform (name, jitsi_url, jilo_database) VALUES (?, ?, ?)'); $stmt = $this->db->getConnection()->prepare('INSERT INTO platforms (name, jitsi_url, jilo_database) VALUES (?, ?, ?)');
$stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']); $stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']);
$platformId = $this->db->getConnection()->lastInsertId(); $platformId = $this->db->getConnection()->lastInsertId();
@ -95,7 +95,7 @@ class PlatformTest extends TestCase
public function testEditPlatform() public function testEditPlatform()
{ {
// Create test platform // Create test platform
$stmt = $this->db->getConnection()->prepare('INSERT INTO platform (name, jitsi_url, jilo_database) VALUES (?, ?, ?)'); $stmt = $this->db->getConnection()->prepare('INSERT INTO platforms (name, jitsi_url, jilo_database) VALUES (?, ?, ?)');
$stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']); $stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']);
$platformId = $this->db->getConnection()->lastInsertId(); $platformId = $this->db->getConnection()->lastInsertId();
@ -109,7 +109,7 @@ class PlatformTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify update // Verify update
$stmt = $this->db->getConnection()->prepare('SELECT * FROM platform WHERE id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT * FROM platforms WHERE id = ?');
$stmt->execute([$platformId]); $stmt->execute([$platformId]);
$platform = $stmt->fetch(PDO::FETCH_ASSOC); $platform = $stmt->fetch(PDO::FETCH_ASSOC);
@ -120,36 +120,36 @@ class PlatformTest extends TestCase
public function testDeletePlatform() public function testDeletePlatform()
{ {
// Create test platform // Create test platform
$stmt = $this->db->getConnection()->prepare('INSERT INTO platform (name, jitsi_url, jilo_database) VALUES (?, ?, ?)'); $stmt = $this->db->getConnection()->prepare('INSERT INTO platforms (name, jitsi_url, jilo_database) VALUES (?, ?, ?)');
$stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']); $stmt->execute(['Test platform', 'https://jitsi.example.com', '/path/to/jilo.db']);
$platformId = $this->db->getConnection()->lastInsertId(); $platformId = $this->db->getConnection()->lastInsertId();
// Create test host // Create test host
$stmt = $this->db->getConnection()->prepare('INSERT INTO host (platform_id, name) VALUES (?, ?)'); $stmt = $this->db->getConnection()->prepare('INSERT INTO hosts (platform_id, name) VALUES (?, ?)');
$stmt->execute([$platformId, 'Test host']); $stmt->execute([$platformId, 'Test host']);
$hostId = $this->db->getConnection()->lastInsertId(); $hostId = $this->db->getConnection()->lastInsertId();
// Create test agent // Create test agent
$stmt = $this->db->getConnection()->prepare('INSERT INTO jilo_agent (host_id) VALUES (?)'); $stmt = $this->db->getConnection()->prepare('INSERT INTO jilo_agents (host_id) VALUES (?)');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$result = $this->platform->deletePlatform($platformId); $result = $this->platform->deletePlatform($platformId);
$this->assertTrue($result); $this->assertTrue($result);
// Verify platform deletion // Verify platform deletion
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platform WHERE id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE id = ?');
$stmt->execute([$platformId]); $stmt->execute([$platformId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(0, $result['count']); $this->assertEquals(0, $result['count']);
// Verify host deletion // Verify host deletion
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM host WHERE platform_id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM hosts WHERE platform_id = ?');
$stmt->execute([$platformId]); $stmt->execute([$platformId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(0, $result['count']); $this->assertEquals(0, $result['count']);
// Verify agent deletion // Verify agent deletion
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM jilo_agent WHERE host_id = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM jilo_agents WHERE host_id = ?');
$stmt->execute([$hostId]); $stmt->execute([$hostId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(0, $result['count']); $this->assertEquals(0, $result['count']);
@ -167,7 +167,7 @@ class PlatformTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify platform was created // Verify platform was created
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platform WHERE name = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE name = ?');
$stmt->execute([$validData['name']]); $stmt->execute([$validData['name']]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(1, $result['count']); $this->assertEquals(1, $result['count']);
@ -182,7 +182,7 @@ class PlatformTest extends TestCase
$this->assertIsString($result); // Should return error message $this->assertIsString($result); // Should return error message
// Verify platform was not created // Verify platform was not created
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platform WHERE name = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE name = ?');
$stmt->execute([$invalidData['name']]); $stmt->execute([$invalidData['name']]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(0, $result['count']); $this->assertEquals(0, $result['count']);
@ -205,7 +205,7 @@ class PlatformTest extends TestCase
$this->assertTrue($result); $this->assertTrue($result);
// Verify platform was created // Verify platform was created
$stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platform WHERE jilo_database = ?'); $stmt = $this->db->getConnection()->prepare('SELECT COUNT(*) as count FROM platforms WHERE jilo_database = ?');
$stmt->execute([$tempDb]); $stmt->execute([$tempDb]);
$result = $stmt->fetch(PDO::FETCH_ASSOC); $result = $stmt->fetch(PDO::FETCH_ASSOC);
$this->assertEquals(1, $result['count']); $this->assertEquals(1, $result['count']);

View File

@ -8,99 +8,103 @@ use PHPUnit\Framework\TestCase;
class RateLimiterTest extends TestCase class RateLimiterTest extends TestCase
{ {
private $db;
private $rateLimiter; private $rateLimiter;
private $db;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
// Prepare DB for Github CI // Set up in-memory SQLite database
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
// Set up test database
$this->db = new Database([ $this->db = new Database([
'type' => 'mariadb', 'type' => 'sqlite',
'host' => $host, 'dbFile' => ':memory:'
'port' => '3306',
'dbname' => 'totalmeet_test',
'user' => 'test_totalmeet',
'password' => $password
]); ]);
// The RateLimiter constructor will create all necessary tables
$this->rateLimiter = new RateLimiter($this->db); $this->rateLimiter = new RateLimiter($this->db);
} }
protected function tearDown(): void
{
// Drop tables in correct order
$this->db->getConnection()->exec("DROP TABLE IF EXISTS {$this->rateLimiter->authRatelimitTable}");
$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->whitelistTable}");
parent::tearDown();
}
public function testGetRecentAttempts() public function testGetRecentAttempts()
{ {
$ip = '8.8.8.8'; $ip = '127.0.0.1';
$username = 'testuser';
// Record some login attempts // Clean up any existing attempts first
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->authRatelimitTable} $stmt = $this->db->getConnection()->prepare("DELETE FROM {$this->rateLimiter->authRatelimitTable} WHERE ip_address = ?");
(ip_address, username, attempted_at) VALUES (?, ?, NOW())"); $stmt->execute([$ip]);
// Add 3 attempts
for ($i = 0; $i < 3; $i++) {
$stmt->execute([$ip, 'testuser']);
}
// Initially should have no attempts
$attempts = $this->rateLimiter->getRecentAttempts($ip); $attempts = $this->rateLimiter->getRecentAttempts($ip);
$this->assertEquals(3, $attempts); $this->assertEquals(0, $attempts);
// Add a login attempt
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->authRatelimitTable} (ip_address, username) VALUES (?, ?)");
$stmt->execute([$ip, $username]);
// Should now have 1 attempt
$attempts = $this->rateLimiter->getRecentAttempts($ip);
$this->assertEquals(1, $attempts);
} }
public function testIsIpBlacklisted() public function testIpBlacklisting()
{ {
$ip = '8.8.8.8'; $ip = '192.0.2.1'; // Using TEST-NET-1 range
// Should be blacklisted by default (TEST-NET-1 range)
$this->assertTrue($this->rateLimiter->isIpBlacklisted($ip));
// Test with non-blacklisted IP
$nonBlacklistedIp = '8.8.8.8'; // Google DNS
$this->assertFalse($this->rateLimiter->isIpBlacklisted($nonBlacklistedIp));
// Add IP to blacklist // Add IP to blacklist
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->blacklistTable} $stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->blacklistTable} (ip_address, reason) VALUES (?, ?)");
(ip_address, is_network, reason) VALUES (?, ?, ?)"); $stmt->execute([$nonBlacklistedIp, 'Test blacklist']);
$stmt->execute([$ip, 0, 'Test blacklist']); // Explicitly set is_network to 0 (false)
$this->assertTrue($this->rateLimiter->isIpBlacklisted($ip)); // IP should now be blacklisted
$this->assertFalse($this->rateLimiter->isIpBlacklisted('8.8.4.4')); $this->assertTrue($this->rateLimiter->isIpBlacklisted($nonBlacklistedIp));
} }
public function testIsIpWhitelisted() public function testIpWhitelisting()
{ {
// Test with an IP that's not in the default whitelisted ranges $ip = '127.0.0.1'; // Localhost
$ip = '8.8.8.8'; // Google's DNS, definitely not in private ranges
// Add IP to whitelist // Clean up any existing whitelist entries
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->whitelistTable} $stmt = $this->db->getConnection()->prepare("DELETE FROM {$this->rateLimiter->whitelistTable} WHERE ip_address = ?");
(ip_address, is_network, description) VALUES (?, ?, ?)"); $stmt->execute([$ip]);
$stmt->execute([$ip, 0, 'Test whitelist']); // Explicitly set is_network to 0 (false)
// Add to whitelist
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->whitelistTable} (ip_address, description) VALUES (?, ?)");
$stmt->execute([$ip, 'Test whitelist']);
// Should be whitelisted
$this->assertTrue($this->rateLimiter->isIpWhitelisted($ip)); $this->assertTrue($this->rateLimiter->isIpWhitelisted($ip));
$this->assertFalse($this->rateLimiter->isIpWhitelisted('8.8.4.4')); // Another IP not in private ranges
// Test with non-whitelisted IP
$nonWhitelistedIp = '8.8.8.8'; // Google DNS
$this->assertFalse($this->rateLimiter->isIpWhitelisted($nonWhitelistedIp));
// Add to whitelist
$stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->whitelistTable} (ip_address, description) VALUES (?, ?)");
$stmt->execute([$nonWhitelistedIp, 'Test whitelist']);
// Should now be whitelisted
$this->assertTrue($this->rateLimiter->isIpWhitelisted($nonWhitelistedIp));
} }
public function testRateLimitCheck() public function testIpRangeBlacklisting()
{ {
$ip = '8.8.8.8'; // Use non-whitelisted IP $ip = '8.8.8.8'; // Google DNS
$endpoint = '/test'; $networkIp = '8.8.8.0/24'; // Network containing Google DNS
// First request should be allowed // Initially IP should not be blacklisted
$this->assertTrue($this->rateLimiter->isPageRequestAllowed($ip, $endpoint)); $this->assertFalse($this->rateLimiter->isIpBlacklisted($ip));
// Add requests up to the limit // Add network to blacklist
for ($i = 0; $i < 60; $i++) { // Default limit is 60 per minute $stmt = $this->db->getConnection()->prepare("INSERT INTO {$this->rateLimiter->blacklistTable} (ip_address, is_network, reason) VALUES (?, 1, ?)");
$this->rateLimiter->recordPageRequest($ip, $endpoint); $stmt->execute([$networkIp, 'Test network blacklist']);
}
// The next request should be rate limited // IP in range should now be blacklisted
$this->assertFalse($this->rateLimiter->isPageRequestAllowed($ip, $endpoint)); $this->assertTrue($this->rateLimiter->isIpBlacklisted($ip));
} }
} }

View File

@ -17,64 +17,78 @@ class UserRegisterTest extends TestCase
{ {
parent::setUp(); parent::setUp();
// Prepare DB for Github CI // Set up test database
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
$this->db = new Database([ $this->db = new Database([
'type' => 'mariadb', 'type' => 'sqlite',
'host' => $host, 'dbFile' => ':memory:'
'port' => '3306',
'dbname' => 'totalmeet_test',
'user' => 'test_totalmeet',
'password' => $password
]); ]);
// Create user table with MariaDB syntax // Create users table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user ( CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(255) NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL, password TEXT NOT NULL
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) )
"); ");
// Create user_meta table with MariaDB syntax // Create users_meta table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user_meta ( CREATE TABLE users_meta (
id INT PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INT NOT NULL, user_id INTEGER NOT NULL,
name VARCHAR(255), name TEXT,
email VARCHAR(255), email TEXT,
timezone VARCHAR(100), timezone TEXT,
bio TEXT, bio TEXT,
avatar VARCHAR(255), avatar TEXT,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(id)
)
");
// Create security_rate_auth table for rate limiting
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_rate_auth (
id INT PRIMARY KEY AUTO_INCREMENT,
ip_address VARCHAR(45) NOT NULL,
username VARCHAR(255) NOT NULL,
attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_ip_username (ip_address, username)
) )
"); ");
// Create user_2fa table for two-factor authentication // Create user_2fa table for two-factor authentication
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user_2fa ( CREATE TABLE user_2fa (
id INT PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INT NOT NULL, user_id INTEGER NOT NULL,
secret_key VARCHAR(255) NOT NULL, secret_key TEXT NOT NULL,
backup_codes TEXT, backup_codes TEXT,
enabled TINYINT(1) NOT NULL DEFAULT 0, enabled TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)
");
// Create tables for rate limiter
$this->db->getConnection()->exec("
CREATE TABLE login_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL,
username TEXT NOT NULL,
attempted_at TEXT DEFAULT (DATETIME('now'))
)
");
$this->db->getConnection()->exec("
CREATE TABLE ip_whitelist (
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
)
");
$this->db->getConnection()->exec("
CREATE TABLE ip_blacklist (
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
) )
"); ");
@ -82,116 +96,91 @@ class UserRegisterTest extends TestCase
$this->user = new User($this->db); $this->user = new User($this->db);
} }
protected function tearDown(): void
{
// Drop tables in correct order
$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 user_meta");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user");
parent::tearDown();
}
public function testRegister() public function testRegister()
{ {
// Register a new user $result = $this->register->register('testuser', 'password123');
$username = 'testuser';
$password = 'password123';
$result = $this->register->register($username, $password);
$this->assertTrue($result); $this->assertTrue($result);
// Verify user was created // Verify user was created
$stmt = $this->db->getConnection()->prepare("SELECT * FROM user WHERE username = ?"); $stmt = $this->db->getConnection()->prepare('SELECT * FROM users WHERE username = ?');
$stmt->execute([$username]); $stmt->execute(['testuser']);
$user = $stmt->fetch(PDO::FETCH_ASSOC); $user = $stmt->fetch(\PDO::FETCH_ASSOC);
$this->assertNotNull($user); $this->assertEquals('testuser', $user['username']);
$this->assertEquals($username, $user['username']); $this->assertTrue(password_verify('password123', $user['password']));
$this->assertTrue(password_verify($password, $user['password']));
// Verify metadata was created // Verify user_meta was created
$stmt = $this->db->getConnection()->prepare("SELECT * FROM user_meta WHERE user_id = ?"); $stmt = $this->db->getConnection()->prepare('SELECT * FROM users_meta WHERE user_id = ?');
$stmt->execute([$user['id']]); $stmt->execute([$user['id']]);
$meta = $stmt->fetch(PDO::FETCH_ASSOC); $meta = $stmt->fetch(\PDO::FETCH_ASSOC);
$this->assertNotNull($meta); $this->assertNotNull($meta);
$this->assertEquals($user['id'], $meta['user_id']);
} }
public function testLogin() public function testLogin()
{ {
// First register a user // Create a test user
$username = 'testuser';
$password = 'password123'; $password = 'password123';
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$this->register->register($username, $password); $stmt = $this->db->getConnection()->prepare('INSERT INTO users (username, password) VALUES (?, ?)');
$stmt->execute(['testuser', $hashedPassword]);
// Mock $_SERVER['REMOTE_ADDR'] for rate limiter // Mock $_SERVER['REMOTE_ADDR'] for rate limiter
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Test successful login // Test successful login
try { try {
$result = $this->user->login($username, $password); $result = $this->user->login('testuser', $password);
$this->assertIsArray($result); $this->assertIsArray($result);
$this->assertEquals('success', $result['status']); $this->assertEquals('success', $result['status']);
$this->assertArrayHasKey('user_id', $result); $this->assertArrayHasKey('user_id', $result);
$this->assertArrayHasKey('username', $result); $this->assertArrayHasKey('username', $result);
$this->assertArrayHasKey('user_id', $_SESSION); $this->assertArrayHasKey('user_id', $_SESSION);
$this->assertArrayHasKey('username', $_SESSION);
$this->assertArrayHasKey('CREATED', $_SESSION); $this->assertArrayHasKey('CREATED', $_SESSION);
$this->assertArrayHasKey('LAST_ACTIVITY', $_SESSION); $this->assertArrayHasKey('LAST_ACTIVITY', $_SESSION);
} catch (Exception $e) { } catch (Exception $e) {
$this->fail('Login should not throw for valid credentials: ' . $e->getMessage()); $this->fail('Login should not throw an exception for valid credentials: ' . $e->getMessage());
} }
// Test failed login // Test failed login
$result = $this->user->login($username, 'wrongpassword'); try {
$this->assertIsArray($result); $this->user->login('testuser', 'wrongpassword');
$this->assertEquals('failed', $result['status']); $this->fail('Login should throw an exception for invalid credentials');
$this->assertArrayHasKey('message', $result); } catch (Exception $e) {
$this->assertStringContainsString('Invalid credentials', $result['message']); $this->assertStringContainsString('Invalid credentials', $e->getMessage());
}
// Test nonexistent user
try {
$this->user->login('nonexistent', $password);
$this->fail('Login should throw an exception for nonexistent user');
} catch (Exception $e) {
$this->assertStringContainsString('Invalid credentials', $e->getMessage());
}
} }
public function testGetUserDetails() public function testGetUserDetails()
{ {
// Register a test user first // Create a test user
$username = 'testuser'; $stmt = $this->db->getConnection()->prepare('INSERT INTO users (username, password) VALUES (?, ?)');
$password = 'password123'; $stmt->execute(['testuser', 'hashedpassword']);
$result = $this->register->register($username, $password); $userId = $this->db->getConnection()->lastInsertId();
$this->assertTrue($result);
// Get user ID from database // Create user meta with some data
$stmt = $this->db->getConnection()->prepare("SELECT id FROM user WHERE username = ?"); $stmt = $this->db->getConnection()->prepare('INSERT INTO users_meta (user_id, name, email) VALUES (?, ?, ?)');
$stmt->execute([$username]); $stmt->execute([$userId, 'Test User', 'test@example.com']);
$userId = $stmt->fetchColumn();
$this->assertNotFalse($userId);
// Insert user metadata
$stmt = $this->db->getConnection()->prepare("
UPDATE user_meta
SET name = ?, email = ?
WHERE user_id = ?
");
$stmt->execute(['Test User', 'test@example.com', $userId]);
// Get user details
$userDetails = $this->user->getUserDetails($userId); $userDetails = $this->user->getUserDetails($userId);
$this->assertIsArray($userDetails); $this->assertIsArray($userDetails);
$this->assertNotEmpty($userDetails); $this->assertCount(1, $userDetails); // Should return one row
$this->assertArrayHasKey(0, $userDetails, 'User details should be returned as an array'); $user = $userDetails[0]; // Get the first row
$this->assertEquals('testuser', $user['username']);
$this->assertEquals('Test User', $user['name']);
$this->assertEquals('test@example.com', $user['email']);
// Get first row since we're querying by primary key // Test nonexistent user
$userDetails = $userDetails[0]; $userDetails = $this->user->getUserDetails(999);
$this->assertEmpty($userDetails);
$this->assertArrayHasKey('username', $userDetails, 'User details should include username');
$this->assertArrayHasKey('name', $userDetails, 'User details should include name');
$this->assertArrayHasKey('email', $userDetails, 'User details should include email');
// Verify values
$this->assertEquals($username, $userDetails['username'], 'Username should match');
$this->assertEquals('Test User', $userDetails['name'], 'Name should match');
$this->assertEquals('test@example.com', $userDetails['email'], 'Email should match');
} }
} }

View File

@ -2,7 +2,6 @@
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) . '/app/classes/ratelimiter.php'; require_once dirname(__DIR__, 3) . '/app/classes/ratelimiter.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -10,161 +9,154 @@ use PHPUnit\Framework\TestCase;
class UserTest extends TestCase class UserTest extends TestCase
{ {
private $db; private $db;
private $register;
private $user; private $user;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
// Prepare DB for Github CI // Set up test database
$host = defined('CI_DB_HOST') ? CI_DB_HOST : '127.0.0.1';
$password = defined('CI_DB_PASSWORD') ? CI_DB_PASSWORD : '';
$this->db = new Database([ $this->db = new Database([
'type' => 'mariadb', 'type' => 'sqlite',
'host' => $host, 'dbFile' => ':memory:'
'port' => '3306',
'dbname' => 'totalmeet_test',
'user' => 'test_totalmeet',
'password' => $password
]); ]);
// Create user table with MariaDB syntax // Create users table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user ( CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(255) NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL, password TEXT NOT NULL
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) )
"); ");
// Create user_meta table with MariaDB syntax // Create users_meta table
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user_meta ( CREATE TABLE users_meta (
id INT PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INT NOT NULL, user_id INTEGER NOT NULL,
name VARCHAR(255), name TEXT,
email VARCHAR(255), email TEXT,
timezone VARCHAR(100), timezone TEXT,
bio TEXT, bio TEXT,
avatar VARCHAR(255), avatar TEXT,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(id)
)
");
// Create security_rate_auth table for rate limiting
$this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS security_rate_auth (
id INT PRIMARY KEY AUTO_INCREMENT,
ip_address VARCHAR(45) NOT NULL,
username VARCHAR(255) NOT NULL,
attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_ip_username (ip_address, username)
) )
"); ");
// Create user_2fa table for two-factor authentication // Create user_2fa table for two-factor authentication
$this->db->getConnection()->exec(" $this->db->getConnection()->exec("
CREATE TABLE IF NOT EXISTS user_2fa ( CREATE TABLE user_2fa (
id INT PRIMARY KEY AUTO_INCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INT NOT NULL, user_id INTEGER NOT NULL,
secret_key VARCHAR(255) NOT NULL, secret_key TEXT NOT NULL,
backup_codes TEXT, backup_codes TEXT,
enabled TINYINT(1) NOT NULL DEFAULT 0, enabled TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)
");
// Create tables for rate limiter
$this->db->getConnection()->exec("
CREATE TABLE login_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL,
username TEXT NOT NULL,
attempted_at TEXT DEFAULT (DATETIME('now'))
)
");
$this->db->getConnection()->exec("
CREATE TABLE ip_whitelist (
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
)
");
$this->db->getConnection()->exec("
CREATE TABLE ip_blacklist (
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
) )
"); ");
$this->user = new User($this->db); $this->user = new User($this->db);
$this->register = new Register($this->db);
}
protected function tearDown(): void
{
// Drop tables in correct order
$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 user_meta");
$this->db->getConnection()->exec("DROP TABLE IF EXISTS user");
parent::tearDown();
} }
public function testLogin() public function testLogin()
{ {
// First register a user // Create a test user
$username = 'testuser';
$password = 'password123'; $password = 'password123';
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$this->register->register($username, $password); $stmt = $this->db->getConnection()->prepare('INSERT INTO users (username, password) VALUES (?, ?)');
$stmt->execute(['testuser', $hashedPassword]);
// Mock $_SERVER['REMOTE_ADDR'] for rate limiter // Mock $_SERVER['REMOTE_ADDR'] for rate limiter
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Test successful login // Test successful login
try { try {
$result = $this->user->login($username, $password); $result = $this->user->login('testuser', $password);
$this->assertIsArray($result); $this->assertIsArray($result);
$this->assertEquals('success', $result['status']); $this->assertEquals('success', $result['status']);
$this->assertArrayHasKey('user_id', $result); $this->assertArrayHasKey('user_id', $result);
$this->assertArrayHasKey('username', $result); $this->assertArrayHasKey('username', $result);
$this->assertArrayHasKey('user_id', $_SESSION); $this->assertArrayHasKey('user_id', $_SESSION);
$this->assertArrayHasKey('username', $_SESSION);
$this->assertArrayHasKey('CREATED', $_SESSION); $this->assertArrayHasKey('CREATED', $_SESSION);
$this->assertArrayHasKey('LAST_ACTIVITY', $_SESSION); $this->assertArrayHasKey('LAST_ACTIVITY', $_SESSION);
} catch (Exception $e) { } catch (Exception $e) {
$this->fail('Login should not throw for valid credentials: ' . $e->getMessage()); $this->fail('Login should not throw an exception for valid credentials: ' . $e->getMessage());
} }
// Test failed login // Test failed login
$result = $this->user->login($username, 'wrongpassword'); try {
$this->assertIsArray($result); $this->user->login('testuser', 'wrongpassword');
$this->assertEquals('failed', $result['status']); $this->fail('Login should throw an exception for invalid credentials');
$this->assertArrayHasKey('message', $result); } catch (Exception $e) {
$this->assertStringContainsString('Invalid credentials', $result['message']); $this->assertStringContainsString('Invalid credentials', $e->getMessage());
}
// Test nonexistent user
try {
$this->user->login('nonexistent', $password);
$this->fail('Login should throw an exception for nonexistent user');
} catch (Exception $e) {
$this->assertStringContainsString('Invalid credentials', $e->getMessage());
}
} }
public function testGetUserDetails() public function testGetUserDetails()
{ {
// Register a test user first // Create a test user
$username = 'testuser'; $stmt = $this->db->getConnection()->prepare('INSERT INTO users (username, password) VALUES (?, ?)');
$password = 'password123'; $stmt->execute(['testuser', 'hashedpassword']);
$result = $this->register->register($username, $password); $userId = $this->db->getConnection()->lastInsertId();
$this->assertTrue($result);
// Get user ID from database // Create user meta with some data
$stmt = $this->db->getConnection()->prepare("SELECT id FROM user WHERE username = ?"); $stmt = $this->db->getConnection()->prepare('INSERT INTO users_meta (user_id, name, email) VALUES (?, ?, ?)');
$stmt->execute([$username]); $stmt->execute([$userId, 'Test User', 'test@example.com']);
$userId = $stmt->fetchColumn();
$this->assertNotFalse($userId);
// Insert user metadata
$stmt = $this->db->getConnection()->prepare("
UPDATE user_meta
SET name = ?, email = ?
WHERE user_id = ?
");
$stmt->execute(['Test User', 'test@example.com', $userId]);
// Get user details
$userDetails = $this->user->getUserDetails($userId); $userDetails = $this->user->getUserDetails($userId);
$this->assertIsArray($userDetails); $this->assertIsArray($userDetails);
$this->assertNotEmpty($userDetails); $this->assertCount(1, $userDetails); // Should return one row
$this->assertArrayHasKey(0, $userDetails, 'User details should be returned as an array'); $user = $userDetails[0]; // Get the first row
$this->assertEquals('testuser', $user['username']);
$this->assertEquals('Test User', $user['name']);
$this->assertEquals('test@example.com', $user['email']);
// Get first row since we're querying by primary key // Test nonexistent user
$userDetails = $userDetails[0]; $userDetails = $this->user->getUserDetails(999);
$this->assertEmpty($userDetails);
$this->assertArrayHasKey('username', $userDetails, 'User details should include username');
$this->assertArrayHasKey('name', $userDetails, 'User details should include name');
$this->assertArrayHasKey('email', $userDetails, 'User details should include email');
// Verify values
$this->assertEquals($username, $userDetails['username'], 'Username should match');
$this->assertEquals('Test User', $userDetails['name'], 'Name should match');
$this->assertEquals('test@example.com', $userDetails['email'], 'Email should match');
} }
} }

View File

@ -12,19 +12,9 @@ if (!headers_sent()) {
ini_set('session.gc_maxlifetime', 1440); // 24 minutes ini_set('session.gc_maxlifetime', 1440); // 24 minutes
} }
// Load plugin Log model and IP helper early so fallback wrapper is bypassed
require_once __DIR__ . '/../app/helpers/ip_helper.php';
// Initialize global user_IP for tests
global $user_IP;
$user_IP = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
// Load Composer's autoloader // Load Composer's autoloader
require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/vendor/autoload.php';
// Ensure core NullLogger is available during tests
require_once __DIR__ . '/../app/core/NullLogger.php';
// Set error reporting // Set error reporting
error_reporting(E_ALL); error_reporting(E_ALL);
ini_set('display_errors', 1); ini_set('display_errors', 1);
@ -36,23 +26,18 @@ date_default_timezone_set('UTC');
// Define global variables needed by the application // Define global variables needed by the application
$GLOBALS['app_root'] = '/'; $GLOBALS['app_root'] = '/';
$GLOBALS['config'] = [ $GLOBALS['config'] = [
'db_type' => 'mariadb', 'db' => [
'sql' => [ 'type' => 'sqlite',
'sql_host' => 'localhost', 'dbFile' => ':memory:'
'sql_port' => '3306', ]
'sql_database' => 'jilo_test',
'sql_username' => 'test_jilo',
'sql_password' => '',
],
'environment' => 'testing'
]; ];
// Define global connectDB function // Define global connectDB function
if (!function_exists('connectDB')) { if (!function_exists('connectDB')) {
function connectDB($config) { function connectDB($config) {
global $db; global $dbWeb;
return [ return [
'db' => $db 'db' => $dbWeb
]; ];
} }
} }