Implements and troubleshoots new messages system
parent
4bfae911db
commit
b314cdd14d
|
@ -7,106 +7,136 @@ class Messages {
|
||||||
const TYPE_INFO = 'info';
|
const TYPE_INFO = 'info';
|
||||||
const TYPE_WARNING = 'warning';
|
const TYPE_WARNING = 'warning';
|
||||||
|
|
||||||
// Message categories
|
// Default message configurations
|
||||||
const SECURITY = [
|
const NOTICE = [
|
||||||
'WHITELIST_ADD_SUCCESS' => [
|
'DEFAULT' => [
|
||||||
'message' => 'IP address successfully added to whitelist.',
|
|
||||||
'type' => self::TYPE_SUCCESS,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'WHITELIST_ADD_ERROR' => [
|
|
||||||
'message' => 'Failed to add IP to whitelist. Please check the IP format.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'WHITELIST_REMOVE_SUCCESS' => [
|
|
||||||
'message' => 'IP address successfully removed from whitelist.',
|
|
||||||
'type' => self::TYPE_SUCCESS,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'WHITELIST_REMOVE_ERROR' => [
|
|
||||||
'message' => 'Failed to remove IP from whitelist.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'BLACKLIST_ADD_SUCCESS' => [
|
|
||||||
'message' => 'IP address successfully added to blacklist.',
|
|
||||||
'type' => self::TYPE_SUCCESS,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'BLACKLIST_ADD_ERROR' => [
|
|
||||||
'message' => 'Failed to add IP to blacklist. Please check the IP format.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'BLACKLIST_REMOVE_SUCCESS' => [
|
|
||||||
'message' => 'IP address successfully removed from blacklist.',
|
|
||||||
'type' => self::TYPE_SUCCESS,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'BLACKLIST_REMOVE_ERROR' => [
|
|
||||||
'message' => 'Failed to remove IP from blacklist.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'IP_REQUIRED' => [
|
|
||||||
'message' => 'IP address is required.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => true
|
|
||||||
],
|
|
||||||
'PERMISSION_DENIED' => [
|
|
||||||
'message' => 'You do not have permission to perform this action.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => false
|
|
||||||
],
|
|
||||||
'RATE_LIMIT_INFO' => [
|
|
||||||
'message' => 'Rate limiting is active. This helps protect against brute force attacks.',
|
|
||||||
'type' => self::TYPE_INFO,
|
'type' => self::TYPE_INFO,
|
||||||
|
'dismissible' => true
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
const ERROR = [
|
||||||
|
'DEFAULT' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
'dismissible' => false
|
'dismissible' => false
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
const LOGIN = [
|
const LOGIN = [
|
||||||
'LOGIN_FAILED' => [
|
'LOGIN_SUCCESS' => [
|
||||||
'message' => 'Invalid username or password.',
|
'type' => self::TYPE_SUCCESS,
|
||||||
'type' => self::TYPE_ERROR,
|
|
||||||
'dismissible' => true
|
'dismissible' => true
|
||||||
],
|
],
|
||||||
'LOGIN_BLOCKED' => [
|
'LOGIN_FAILED' => [
|
||||||
'message' => 'Too many failed attempts. Please try again later.',
|
|
||||||
'type' => self::TYPE_ERROR,
|
'type' => self::TYPE_ERROR,
|
||||||
'dismissible' => false
|
'dismissible' => false
|
||||||
],
|
],
|
||||||
|
'LOGOUT_SUCCESS' => [
|
||||||
|
'type' => self::TYPE_SUCCESS,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
'IP_BLACKLISTED' => [
|
'IP_BLACKLISTED' => [
|
||||||
'message' => 'Access denied. Your IP address is blacklisted.',
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => false
|
||||||
|
],
|
||||||
|
'IP_NOT_WHITELISTED' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => false
|
||||||
|
],
|
||||||
|
'TOO_MANY_ATTEMPTS' => [
|
||||||
'type' => self::TYPE_ERROR,
|
'type' => self::TYPE_ERROR,
|
||||||
'dismissible' => false
|
'dismissible' => false
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const SECURITY = [
|
||||||
|
'WHITELIST_ADD_SUCCESS' => [
|
||||||
|
'type' => self::TYPE_SUCCESS,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'WHITELIST_ADD_ERROR' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'WHITELIST_REMOVE_SUCCESS' => [
|
||||||
|
'type' => self::TYPE_SUCCESS,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'WHITELIST_REMOVE_ERROR' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'BLACKLIST_ADD_SUCCESS' => [
|
||||||
|
'type' => self::TYPE_SUCCESS,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'BLACKLIST_ADD_ERROR' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'BLACKLIST_REMOVE_SUCCESS' => [
|
||||||
|
'type' => self::TYPE_SUCCESS,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'BLACKLIST_REMOVE_ERROR' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'RATE_LIMIT_INFO' => [
|
||||||
|
'type' => self::TYPE_INFO,
|
||||||
|
'dismissible' => false
|
||||||
|
],
|
||||||
|
'PERMISSION_DENIED' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => false
|
||||||
|
],
|
||||||
|
'IP_REQUIRED' => [
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
private static $strings = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get message strings
|
||||||
|
*/
|
||||||
|
private static function getStrings() {
|
||||||
|
if (self::$strings === null) {
|
||||||
|
self::$strings = require __DIR__ . '/../includes/messages-strings.php';
|
||||||
|
}
|
||||||
|
return self::$strings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get message configuration by key
|
* Get message configuration by key
|
||||||
*/
|
*/
|
||||||
public static function get($category, $key) {
|
public static function get($category, $key) {
|
||||||
$messages = constant("self::$category");
|
$config = constant("self::$category")[$key] ?? null;
|
||||||
return $messages[$key] ?? null;
|
if (!$config) return null;
|
||||||
|
|
||||||
|
$strings = self::getStrings();
|
||||||
|
$message = $strings[$category][$key] ?? '';
|
||||||
|
|
||||||
|
return array_merge($config, ['message' => $message]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render message HTML
|
* Render message HTML
|
||||||
*/
|
*/
|
||||||
public static function render($category, $key, $customMessage = null) {
|
public static function render($category, $key, $customMessage = null, $dismissible = null) {
|
||||||
$config = self::get($category, $key);
|
$config = self::get($category, $key);
|
||||||
if (!$config) return '';
|
if (!$config) return '';
|
||||||
|
|
||||||
$message = $customMessage ?? $config['message'];
|
$message = $customMessage ?? $config['message'];
|
||||||
$dismissible = $config['dismissible'] ? ' alert-dismissible fade show' : '';
|
$isDismissible = $dismissible ?? $config['dismissible'];
|
||||||
$dismissButton = $config['dismissible'] ? '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' : '';
|
$dismissClass = $isDismissible ? ' alert-dismissible fade show' : '';
|
||||||
|
$dismissButton = $isDismissible ? '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' : '';
|
||||||
|
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'<div class="alert alert-%s%s" role="alert">%s%s</div>',
|
'<div class="alert alert-%s%s" role="alert">%s%s</div>',
|
||||||
$config['type'],
|
$config['type'],
|
||||||
$dismissible,
|
$dismissClass,
|
||||||
htmlspecialchars($message),
|
htmlspecialchars($message),
|
||||||
$dismissButton
|
$dismissButton
|
||||||
);
|
);
|
||||||
|
@ -115,14 +145,15 @@ class Messages {
|
||||||
/**
|
/**
|
||||||
* Store message in session for display after redirect
|
* Store message in session for display after redirect
|
||||||
*/
|
*/
|
||||||
public static function flash($category, $key, $customMessage = null) {
|
public static function flash($category, $key, $customMessage = null, $dismissible = null) {
|
||||||
if (!isset($_SESSION['flash_messages'])) {
|
if (!isset($_SESSION['flash_messages'])) {
|
||||||
$_SESSION['flash_messages'] = [];
|
$_SESSION['flash_messages'] = [];
|
||||||
}
|
}
|
||||||
$_SESSION['flash_messages'][] = [
|
$_SESSION['flash_messages'][] = [
|
||||||
'category' => $category,
|
'category' => $category,
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'custom_message' => $customMessage
|
'custom_message' => $customMessage,
|
||||||
|
'dismissible' => $dismissible
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
class RateLimiter {
|
class RateLimiter {
|
||||||
public $db;
|
public $db;
|
||||||
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 $ratelimitTable = 'login_attempts';
|
public $ratelimitTable = 'login_attempts';
|
||||||
|
@ -22,7 +22,7 @@ class RateLimiter {
|
||||||
// Login attempts table
|
// Login attempts table
|
||||||
$sql = "CREATE TABLE IF NOT EXISTS {$this->ratelimitTable} (
|
$sql = "CREATE TABLE IF NOT EXISTS {$this->ratelimitTable} (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
ip_address TEXT NOT NULL,
|
ip_address TEXT NOT NULL UNIQUE,
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
attempted_at TEXT DEFAULT (DATETIME('now'))
|
attempted_at TEXT DEFAULT (DATETIME('now'))
|
||||||
)";
|
)";
|
||||||
|
@ -45,7 +45,7 @@ class RateLimiter {
|
||||||
ip_address TEXT NOT NULL UNIQUE,
|
ip_address TEXT NOT NULL UNIQUE,
|
||||||
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||||
reason TEXT,
|
reason TEXT,
|
||||||
expiry_time TEXT NULL,
|
expiry_time TEXT NULL,
|
||||||
created_at TEXT DEFAULT (DATETIME('now')),
|
created_at TEXT DEFAULT (DATETIME('now')),
|
||||||
created_by TEXT
|
created_by TEXT
|
||||||
)";
|
)";
|
||||||
|
@ -53,11 +53,11 @@ class RateLimiter {
|
||||||
|
|
||||||
// Default IPs to whitelist (local interface and private networks IPs)
|
// Default IPs to whitelist (local interface and private networks IPs)
|
||||||
$defaultIps = [
|
$defaultIps = [
|
||||||
['127.0.0.1', 0, 'localhost IPv4'],
|
['127.0.0.1', false, 'localhost IPv4'],
|
||||||
['::1', 0, 'localhost IPv6'],
|
['::1', false, 'localhost IPv6'],
|
||||||
['10.0.0.0/8', 1, 'Private network (Class A)'],
|
['10.0.0.0/8', true, 'Private network (Class A)'],
|
||||||
['172.16.0.0/12', 1, 'Private network (Class B)'],
|
['172.16.0.0/12', true, 'Private network (Class B)'],
|
||||||
['192.168.0.0/16', 1, 'Private network (Class C)']
|
['192.168.0.0/16', true, 'Private network (Class C)']
|
||||||
];
|
];
|
||||||
|
|
||||||
// Insert default whitelisted IPs if they don't exist
|
// Insert default whitelisted IPs if they don't exist
|
||||||
|
@ -70,11 +70,11 @@ class RateLimiter {
|
||||||
|
|
||||||
// Insert known malicious networks
|
// Insert known malicious networks
|
||||||
$defaultBlacklist = [
|
$defaultBlacklist = [
|
||||||
['0.0.0.0/8', 1, 'Reserved address space - RFC 1122'],
|
['0.0.0.0/8', true, 'Reserved address space - RFC 1122'],
|
||||||
['100.64.0.0/10', 1, 'Carrier-grade NAT space - RFC 6598'],
|
['100.64.0.0/10', true, 'Carrier-grade NAT space - RFC 6598'],
|
||||||
['192.0.2.0/24', 1, 'TEST-NET-1 Documentation space - RFC 5737'],
|
['192.0.2.0/24', true, 'TEST-NET-1 Documentation space - RFC 5737'],
|
||||||
['198.51.100.0/24', 1, 'TEST-NET-2 Documentation space - RFC 5737'],
|
['198.51.100.0/24', true, 'TEST-NET-2 Documentation space - RFC 5737'],
|
||||||
['203.0.113.0/24', 1, '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 OR IGNORE INTO {$this->blacklistTable}
|
$stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->blacklistTable}
|
||||||
|
@ -87,27 +87,21 @@ class RateLimiter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isIpWhitelisted($ip) {
|
/**
|
||||||
// Check exact IP match and CIDR ranges
|
* Get number of recent login attempts for an IP
|
||||||
$stmt = $this->db->prepare("SELECT ip_address, is_network FROM {$this->whitelistTable}");
|
*/
|
||||||
$stmt->execute();
|
public function getRecentAttempts($ip) {
|
||||||
|
$stmt = $this->db->prepare("SELECT COUNT(*) as attempts FROM {$this->ratelimitTable}
|
||||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
WHERE ip_address = ? AND attempted_at > datetime('now', '-' || :minutes || ' minutes')");
|
||||||
if ($row['is_network']) {
|
$stmt->execute([$ip, $this->decayMinutes]);
|
||||||
if ($this->ipInRange($ip, $row['ip_address'])) {
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
return true;
|
return intval($result['attempts']);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($ip === $row['ip_address']) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isIpBlacklisted($ip) {
|
/**
|
||||||
|
* Check if an IP is blacklisted
|
||||||
|
*/
|
||||||
|
public function isIpBlacklisted($ip) {
|
||||||
// First check if IP is explicitly blacklisted or in a blacklisted range
|
// First check if IP is explicitly blacklisted or in a blacklisted range
|
||||||
$stmt = $this->db->prepare("SELECT ip_address, is_network, expiry_time FROM {$this->blacklistTable}");
|
$stmt = $this->db->prepare("SELECT ip_address, is_network, expiry_time FROM {$this->blacklistTable}");
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
@ -132,6 +126,29 @@ class RateLimiter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an IP is whitelisted
|
||||||
|
*/
|
||||||
|
public function isIpWhitelisted($ip) {
|
||||||
|
// Check exact IP match and CIDR ranges
|
||||||
|
$stmt = $this->db->prepare("SELECT ip_address, is_network FROM {$this->whitelistTable}");
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
if ($row['is_network']) {
|
||||||
|
if ($this->ipInRange($ip, $row['ip_address'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($ip === $row['ip_address']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private function ipInRange($ip, $cidr) {
|
private function ipInRange($ip, $cidr) {
|
||||||
list($subnet, $bits) = explode('/', $cidr);
|
list($subnet, $bits) = explode('/', $cidr);
|
||||||
|
|
||||||
|
@ -151,6 +168,7 @@ class RateLimiter {
|
||||||
$message = "Cannot whitelist {$ip} - IP is currently blacklisted";
|
$message = "Cannot whitelist {$ip} - IP is currently blacklisted";
|
||||||
if ($userId) {
|
if ($userId) {
|
||||||
$this->log->insertLog($userId, "IP Whitelist: {$message}", 'system');
|
$this->log->insertLog($userId, "IP Whitelist: {$message}", 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', $message);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -177,6 +195,7 @@ class RateLimiter {
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($userId) {
|
if ($userId) {
|
||||||
$this->log->insertLog($userId, "IP Whitelist: Failed to add {$ip}: " . $e->getMessage(), 'system');
|
$this->log->insertLog($userId, "IP Whitelist: Failed to add {$ip}: " . $e->getMessage(), 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', "IP Whitelist: Failed to add {$ip}: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -211,6 +230,7 @@ class RateLimiter {
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($userId) {
|
if ($userId) {
|
||||||
$this->log->insertLog($userId, "IP Whitelist: Failed to remove {$ip}: " . $e->getMessage(), 'system');
|
$this->log->insertLog($userId, "IP Whitelist: Failed to remove {$ip}: " . $e->getMessage(), 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', "IP Whitelist: Failed to remove {$ip}: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -223,6 +243,7 @@ class RateLimiter {
|
||||||
$message = "Cannot blacklist {$ip} - IP is currently whitelisted";
|
$message = "Cannot blacklist {$ip} - IP is currently whitelisted";
|
||||||
if ($userId) {
|
if ($userId) {
|
||||||
$this->log->insertLog($userId, "IP Blacklist: {$message}", 'system');
|
$this->log->insertLog($userId, "IP Blacklist: {$message}", 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', $message);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -251,6 +272,7 @@ class RateLimiter {
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($userId) {
|
if ($userId) {
|
||||||
$this->log->insertLog($userId, "IP Blacklist: Failed to add {$ip}: " . $e->getMessage(), 'system');
|
$this->log->insertLog($userId, "IP Blacklist: Failed to add {$ip}: " . $e->getMessage(), 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', "IP Blacklist: Failed to add {$ip}: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -283,6 +305,7 @@ class RateLimiter {
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($userId) {
|
if ($userId) {
|
||||||
$this->log->insertLog($userId, "IP Blacklist: Failed to remove {$ip}: " . $e->getMessage(), 'system');
|
$this->log->insertLog($userId, "IP Blacklist: Failed to remove {$ip}: " . $e->getMessage(), 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', "IP Blacklist: Failed to remove {$ip}: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -317,6 +340,7 @@ class RateLimiter {
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->log->insertLog(0, "Failed to cleanup expired entries: " . $e->getMessage(), 'system');
|
$this->log->insertLog(0, "Failed to cleanup expired entries: " . $e->getMessage(), 'system');
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', "Failed to cleanup expired entries: " . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,9 +414,9 @@ class RateLimiter {
|
||||||
|
|
||||||
$stmt = $this->db->prepare($sql);
|
$stmt = $this->db->prepare($sql);
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
':ip' => $ipAddress,
|
':ip' => $ipAddress,
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
':minutes' => $this->decayMinutes
|
':minutes' => $this->decayMinutes
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
@ -405,7 +429,7 @@ class RateLimiter {
|
||||||
|
|
||||||
$stmt = $this->db->prepare($sql);
|
$stmt = $this->db->prepare($sql);
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
':minutes' => $this->decayMinutes
|
':minutes' => $this->decayMinutes
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,9 +442,9 @@ class RateLimiter {
|
||||||
|
|
||||||
$stmt = $this->db->prepare($sql);
|
$stmt = $this->db->prepare($sql);
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
':ip' => $ipAddress,
|
':ip' => $ipAddress,
|
||||||
':username' => $username,
|
':username' => $username,
|
||||||
':minutes' => $this->decayMinutes
|
':minutes' => $this->decayMinutes
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (isset($messages) && is_array($messages)) {
|
||||||
|
foreach ($messages as $msg) {
|
||||||
|
echo Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Message strings for translation
|
||||||
|
return [
|
||||||
|
'LOGIN' => [
|
||||||
|
'LOGIN_SUCCESS' => 'Login successful.',
|
||||||
|
'LOGIN_FAILED' => 'Login failed. Please check your credentials.',
|
||||||
|
'LOGOUT_SUCCESS' => 'Logout successful. You can log in again.',
|
||||||
|
'IP_BLACKLISTED' => 'Access denied. Your IP address is blacklisted.',
|
||||||
|
'IP_NOT_WHITELISTED' => 'Access denied. Your IP address is not whitelisted.',
|
||||||
|
'TOO_MANY_ATTEMPTS' => 'Too many login attempts. Please try again later.',
|
||||||
|
],
|
||||||
|
'SECURITY' => [
|
||||||
|
'WHITELIST_ADD_SUCCESS' => 'IP address successfully added to whitelist.',
|
||||||
|
'WHITELIST_ADD_ERROR' => 'Failed to add IP to whitelist. Please check the IP format.',
|
||||||
|
'WHITELIST_REMOVE_SUCCESS' => 'IP address successfully removed from whitelist.',
|
||||||
|
'WHITELIST_REMOVE_ERROR' => 'Failed to remove IP from whitelist.',
|
||||||
|
'BLACKLIST_ADD_SUCCESS' => 'IP address successfully added to blacklist.',
|
||||||
|
'BLACKLIST_ADD_ERROR' => 'Failed to add IP to blacklist. Please check the IP format.',
|
||||||
|
'BLACKLIST_REMOVE_SUCCESS' => 'IP address successfully removed from blacklist.',
|
||||||
|
'BLACKLIST_REMOVE_ERROR' => 'Failed to remove IP from blacklist.',
|
||||||
|
'RATE_LIMIT_INFO' => 'Rate limiting is active. This helps protect against brute force attacks.',
|
||||||
|
'PERMISSION_DENIED' => 'Permission denied. You do not have the required rights.',
|
||||||
|
'IP_REQUIRED' => 'IP address is required.',
|
||||||
|
],
|
||||||
|
'REGISTER' => [
|
||||||
|
'SUCCESS' => 'Registration successful. You can log in now.',
|
||||||
|
'FAILED' => 'Registration failed: %s',
|
||||||
|
'DISABLED' => 'Registration is disabled.',
|
||||||
|
],
|
||||||
|
'SYSTEM' => [
|
||||||
|
'DB_ERROR' => 'Error connecting to the database: %s',
|
||||||
|
'DB_CONNECT_ERROR' => 'Error connecting to DB: %s',
|
||||||
|
'DB_UNKNOWN_TYPE' => 'Error: unknown database type "%s"',
|
||||||
|
],
|
||||||
|
];
|
|
@ -1,17 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
// Start session if not already started
|
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
|
||||||
session_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include Messages class
|
// Get any flash messages from previous request
|
||||||
require_once __DIR__ . '/../classes/messages.php';
|
|
||||||
|
|
||||||
// Initialize messages array
|
|
||||||
$messages = [];
|
|
||||||
|
|
||||||
// Get any flash messages from previous requests
|
|
||||||
$flash_messages = Messages::getFlash();
|
$flash_messages = Messages::getFlash();
|
||||||
if (!empty($flash_messages)) {
|
if (!empty($flash_messages)) {
|
||||||
$messages = array_merge($messages, $flash_messages);
|
$messages = array_merge($messages, array_map(function($flash) {
|
||||||
|
return [
|
||||||
|
'category' => $flash['category'],
|
||||||
|
'key' => $flash['key'],
|
||||||
|
'custom_message' => $flash['custom_message']
|
||||||
|
];
|
||||||
|
}, $flash_messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
|
@ -21,6 +21,15 @@ if (isset($_REQUEST['until_time'])) {
|
||||||
$until_time = htmlspecialchars($_REQUEST['until_time']);
|
$until_time = htmlspecialchars($_REQUEST['until_time']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitize session vars
|
||||||
|
if (isset($_SESSION)) {
|
||||||
|
foreach ($_SESSION as $key => $value) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$_SESSION[$key] = htmlspecialchars($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// hosts
|
// hosts
|
||||||
if (isset($_POST['address'])) {
|
if (isset($_POST['address'])) {
|
||||||
$address = htmlspecialchars($_POST['address']);
|
$address = htmlspecialchars($_POST['address']);
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
* 3. The most recent 10 conferences.
|
* 3. The most recent 10 conferences.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Get any new messages
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
include '../app/includes/messages-show.php';
|
||||||
|
|
||||||
require '../app/classes/conference.php';
|
require '../app/classes/conference.php';
|
||||||
require '../app/classes/participant.php';
|
require '../app/classes/participant.php';
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,28 @@ try {
|
||||||
// connect to database
|
// connect to database
|
||||||
$dbWeb = connectDB($config);
|
$dbWeb = connectDB($config);
|
||||||
|
|
||||||
|
// Initialize RateLimiter
|
||||||
|
require_once '../app/classes/ratelimiter.php';
|
||||||
|
$rateLimiter = new RateLimiter($dbWeb);
|
||||||
|
|
||||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||||
try {
|
try {
|
||||||
$username = $_POST['username'];
|
$username = $_POST['username'];
|
||||||
$password = $_POST['password'];
|
$password = $_POST['password'];
|
||||||
|
|
||||||
|
// Check if IP is blacklisted
|
||||||
|
if ($rateLimiter->isIpBlacklisted($user_IP)) {
|
||||||
|
throw new Exception(Messages::get('LOGIN', 'IP_BLACKLISTED')['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check rate limiting (but skip if IP is whitelisted)
|
||||||
|
if (!$rateLimiter->isIpWhitelisted($user_IP)) {
|
||||||
|
$attempts = $rateLimiter->getRecentAttempts($user_IP);
|
||||||
|
if ($attempts >= $rateLimiter->maxAttempts) {
|
||||||
|
throw new Exception(Messages::get('LOGIN', 'LOGIN_BLOCKED')['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// login successful
|
// login successful
|
||||||
if ( $userObject->login($username, $password) ) {
|
if ( $userObject->login($username, $password) ) {
|
||||||
// if remember_me is checked, max out the session
|
// if remember_me is checked, max out the session
|
||||||
|
@ -52,32 +69,40 @@ try {
|
||||||
'samesite' => 'Strict'
|
'samesite' => 'Strict'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// redirect to index
|
// Log successful login
|
||||||
$_SESSION['notice'] = "Login successful";
|
|
||||||
$user_id = $userObject->getUserId($username)[0]['id'];
|
$user_id = $userObject->getUserId($username)[0]['id'];
|
||||||
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
|
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
|
||||||
|
|
||||||
|
// Set success message and redirect
|
||||||
|
Messages::flash('LOGIN', 'LOGIN_SUCCESS');
|
||||||
header('Location: ' . htmlspecialchars($app_root));
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
|
} else {
|
||||||
|
throw new Exception(Messages::get('LOGIN', 'LOGIN_FAILED')['message']);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// Log the failed attempt
|
// Log the failed attempt
|
||||||
$error = $e->getMessage();
|
Messages::flash('ERROR', 'DEFAULT', $e->getMessage());
|
||||||
if (isset($username)) {
|
if (isset($username)) {
|
||||||
$user_id = $userObject->getUserId($username)[0]['id'] ?? 0;
|
$user_id = $userObject->getUserId($username)[0]['id'] ?? 0;
|
||||||
$logObject->insertLog($user_id, "Login: Failed login attempt for user \"$username\". IP: $user_IP. Reason: {$error}", 'user');
|
$logObject->insertLog($user_id, "Login: Failed login attempt for user \"$username\". IP: $user_IP. Reason: {$e->getMessage()}", 'user');
|
||||||
}
|
}
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = getError('There was an unexpected error. Please try again.', $e->getMessage());
|
Messages::flash('ERROR', 'DEFAULT', 'There was an unexpected error. Please try again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show configured login message if any
|
||||||
if (!empty($config['login_message'])) {
|
if (!empty($config['login_message'])) {
|
||||||
$notice = $config['login_message'];
|
echo Messages::render('NOTICE', 'DEFAULT', $config['login_message'], false);
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get any new messages
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
include '../app/includes/messages-show.php';
|
||||||
|
|
||||||
|
// Load the template
|
||||||
include '../app/templates/form-login.php';
|
include '../app/templates/form-login.php';
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -8,12 +8,9 @@
|
||||||
* and redirects to the login page on success or displays an error message on failure.
|
* and redirects to the login page on success or displays an error message on failure.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// check if the registration is allowed
|
// registration is allowed, go on
|
||||||
if ($config['registration_enabled'] === true) {
|
if ($config['registration_enabled'] === true) {
|
||||||
|
|
||||||
// clear any previous error messages
|
|
||||||
unset($error);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// connect to database
|
// connect to database
|
||||||
|
@ -28,27 +25,30 @@ if ($config['registration_enabled'] === true) {
|
||||||
|
|
||||||
// redirect to login
|
// redirect to login
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
$_SESSION['notice'] = "Registration successful.<br />You can log in now.";
|
Messages::flash('NOTICE', 'DEFAULT', "Registration successful.<br />You can log in now.");
|
||||||
header('Location: ' . htmlspecialchars($app_root));
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
// registration fail, redirect to login
|
// registration fail, redirect to login
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error'] = "Registration failed. $result";
|
Messages::flash('ERROR', 'DEFAULT', "Registration failed. $result");
|
||||||
header('Location: ' . htmlspecialchars($app_root));
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = $e->getMessage();
|
Messages::flash('ERROR', 'DEFAULT', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
include '../app/templates/block-message.php';
|
// Get any new messages
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
include '../app/includes/messages-show.php';
|
||||||
|
|
||||||
|
// Load the template
|
||||||
include '../app/templates/form-register.php';
|
include '../app/templates/form-register.php';
|
||||||
|
|
||||||
// registration disabled
|
// registration disabled
|
||||||
} else {
|
} else {
|
||||||
$notice = 'Registration is disabled';
|
echo Messages::render('NOTICE', 'DEFAULT', 'Registration is disabled', false);
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -9,15 +9,18 @@ if (!($userObject->hasRight($user_id, 'superuser') ||
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include Messages class
|
if (!isset($currentUser)) {
|
||||||
require_once '../app/classes/messages.php';
|
include '../app/templates/error-unauthorized.php';
|
||||||
|
exit;
|
||||||
// Initialize variables for feedback messages
|
}
|
||||||
$messages = [];
|
|
||||||
|
|
||||||
// Get current section
|
// Get current section
|
||||||
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
||||||
|
|
||||||
|
// Initialize RateLimiter
|
||||||
|
require_once '../app/classes/ratelimiter.php';
|
||||||
|
$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'])) {
|
||||||
$action = $_POST['action'];
|
$action = $_POST['action'];
|
||||||
|
@ -81,6 +84,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$messages[] = ['category' => 'SECURITY', 'key' => 'CUSTOM_ERROR', 'custom_message' => $e->getMessage()];
|
$messages[] = ['category' => 'SECURITY', 'key' => 'CUSTOM_ERROR', 'custom_message' => $e->getMessage()];
|
||||||
|
Messages::flash('SECURITY', 'CUSTOM_ERROR', 'custom_message');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($messages)) {
|
if (empty($messages)) {
|
||||||
|
@ -90,16 +94,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get flash messages from previous request
|
|
||||||
$flash_messages = Messages::getFlash();
|
|
||||||
$messages = array_merge($messages, array_map(function($flash) {
|
|
||||||
return [
|
|
||||||
'category' => $flash['category'],
|
|
||||||
'key' => $flash['key'],
|
|
||||||
'custom_message' => $flash['custom_message']
|
|
||||||
];
|
|
||||||
}, $flash_messages));
|
|
||||||
|
|
||||||
// Always show rate limit info message for rate limiting section
|
// Always show rate limit info message for rate limiting section
|
||||||
if ($section === 'ratelimit') {
|
if ($section === 'ratelimit') {
|
||||||
$messages[] = ['category' => 'SECURITY', 'key' => 'RATE_LIMIT_INFO'];
|
$messages[] = ['category' => 'SECURITY', 'key' => 'RATE_LIMIT_INFO'];
|
||||||
|
@ -109,7 +103,11 @@ if ($section === 'ratelimit') {
|
||||||
$whitelisted = $rateLimiter->getWhitelistedIps();
|
$whitelisted = $rateLimiter->getWhitelistedIps();
|
||||||
$blacklisted = $rateLimiter->getBlacklistedIps();
|
$blacklisted = $rateLimiter->getBlacklistedIps();
|
||||||
|
|
||||||
// Include template
|
// Get any new messages
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
include '../app/includes/messages-show.php';
|
||||||
|
|
||||||
|
// Load the template
|
||||||
include '../app/templates/security.php';
|
include '../app/templates/security.php';
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// Display and clean up session messages
|
|
||||||
foreach (['error', 'notice'] as $type) {
|
|
||||||
if (isset($_SESSION[$type])) {
|
|
||||||
renderMessage($_SESSION[$type], $type, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display standalone messages
|
|
||||||
if (isset($error)) {
|
|
||||||
renderMessage($error, 'error', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($notice)) {
|
|
||||||
renderMessage($notice, 'notice', true);
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -18,6 +18,28 @@ $(document).ready(function(){
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// dismissible messages
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Initialize Bootstrap alerts
|
||||||
|
var alerts = document.querySelectorAll('.alert');
|
||||||
|
alerts.forEach(function(alert) {
|
||||||
|
var closeButton = alert.querySelector('.btn-close');
|
||||||
|
if (closeButton) {
|
||||||
|
closeButton.addEventListener('click', function() {
|
||||||
|
alert.classList.remove('show');
|
||||||
|
setTimeout(function() {
|
||||||
|
alert.remove();
|
||||||
|
}, 150);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -37,3 +37,13 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<?php if (isset($messages) && is_array($messages)): ?>
|
||||||
|
<?php foreach ($messages as $msg): ?>
|
||||||
|
<?= Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null) ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<h2>Security Settings</h2>
|
<h2>Security Settings</h2>
|
||||||
<?php foreach ($messages as $msg): ?>
|
|
||||||
<?= Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null) ?>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) { ?>
|
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) { ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@ -172,7 +169,7 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3>Rate Limiting Settings</h3>
|
<h3>Rate Limiting Settings</h3>
|
||||||
Restricts brute force or flooding attempts at login page.
|
Rate limiting settings control how many failed login attempts are allowed before blocking an IP address.
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
|
@ -224,21 +221,3 @@
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
<!-- /Security Settings -->
|
<!-- /Security Settings -->
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
// Initialize Bootstrap alerts
|
|
||||||
var alerts = document.querySelectorAll('.alert');
|
|
||||||
alerts.forEach(function(alert) {
|
|
||||||
var closeButton = alert.querySelector('.btn-close');
|
|
||||||
if (closeButton) {
|
|
||||||
closeButton.addEventListener('click', function() {
|
|
||||||
alert.classList.remove('show');
|
|
||||||
setTimeout(function() {
|
|
||||||
alert.remove();
|
|
||||||
}, 150);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -18,14 +18,14 @@ ob_start();
|
||||||
// sanitize all input vars that may end up in URLs or forms
|
// sanitize all input vars that may end up in URLs or forms
|
||||||
require '../app/includes/sanitize.php';
|
require '../app/includes/sanitize.php';
|
||||||
|
|
||||||
require '../app/includes/errors.php';
|
// Initialize message system
|
||||||
|
|
||||||
// Include Messages class
|
|
||||||
require_once '../app/classes/messages.php';
|
require_once '../app/classes/messages.php';
|
||||||
|
|
||||||
// Initialize variables for feedback messages
|
|
||||||
$messages = [];
|
$messages = [];
|
||||||
|
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
|
||||||
|
require '../app/includes/errors.php';
|
||||||
|
|
||||||
// error reporting, comment out in production
|
// error reporting, comment out in production
|
||||||
ini_set('display_errors', 1);
|
ini_set('display_errors', 1);
|
||||||
ini_set('display_startup_errors', 1);
|
ini_set('display_startup_errors', 1);
|
||||||
|
@ -104,12 +104,19 @@ if ( !isset($_COOKIE['username']) && ($page !== 'login' && $page !== 'register')
|
||||||
// connect to db of Jilo Web
|
// connect to db of Jilo Web
|
||||||
require '../app/classes/database.php';
|
require '../app/classes/database.php';
|
||||||
require '../app/includes/database.php';
|
require '../app/includes/database.php';
|
||||||
$response = connectDB($config);
|
try {
|
||||||
if ($response['db'] === null) {
|
$response = connectDB($config);
|
||||||
$error .= $response['error'];
|
if (!$response['db']) {
|
||||||
// include '../app/templates/block-message.php';
|
throw new Exception('Could not connect to database: ' . $response['error']);
|
||||||
} else {
|
}
|
||||||
$dbWeb = $response['db'];
|
$dbWeb = $response['db'];
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', getError('Error connecting to the database.', $e->getMessage()));
|
||||||
|
include '../app/templates/page-header.php';
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
include '../app/includes/messages-show.php';
|
||||||
|
include '../app/templates/page-footer.php';
|
||||||
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// start logging
|
// start logging
|
||||||
|
@ -146,38 +153,17 @@ if ($page == 'logout') {
|
||||||
session_destroy();
|
session_destroy();
|
||||||
setcookie('username', "", time() - 100, $config['folder'], $config['domain'], isset($_SERVER['HTTPS']), true);
|
setcookie('username', "", time() - 100, $config['folder'], $config['domain'], isset($_SERVER['HTTPS']), true);
|
||||||
|
|
||||||
$notice = "You were logged out.<br />You can log in again.";
|
// Log successful logout
|
||||||
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||||
$logObject->insertLog($user_id, "Logout: User \"$currentUser\" logged out. IP: $user_IP", 'user');
|
$logObject->insertLog($user_id, "Logout: User \"$currentUser\" logged out. IP: $user_IP", 'user');
|
||||||
|
|
||||||
|
// Set success message
|
||||||
|
Messages::flash('LOGIN', 'LOGOUT_SUCCESS');
|
||||||
|
|
||||||
include '../app/templates/page-header.php';
|
include '../app/templates/page-header.php';
|
||||||
include '../app/templates/page-menu.php';
|
include '../app/templates/page-menu.php';
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
include '../app/pages/login.php';
|
include '../app/pages/login.php';
|
||||||
|
|
||||||
} elseif ($page === 'security') {
|
|
||||||
// Security settings require login
|
|
||||||
if (!isset($currentUser)) {
|
|
||||||
include '../app/templates/error-unauthorized.php';
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user details and rights
|
|
||||||
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
|
||||||
$userDetails = $userObject->getUserDetails($user_id);
|
|
||||||
$userRights = $userObject->getUserRights($user_id);
|
|
||||||
$userTimezone = isset($userDetails[0]['timezone']) ? $userDetails[0]['timezone'] : 'UTC';
|
|
||||||
|
|
||||||
// Initialize RateLimiter
|
|
||||||
require_once '../app/classes/ratelimiter.php';
|
|
||||||
$rateLimiter = new RateLimiter($dbWeb);
|
|
||||||
|
|
||||||
include '../app/templates/page-header.php';
|
|
||||||
include '../app/templates/page-menu.php';
|
|
||||||
include '../app/templates/page-sidebar.php';
|
|
||||||
include '../app/pages/security.php';
|
|
||||||
include '../app/templates/page-footer.php';
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// if user is logged in, we need user details and rights
|
// if user is logged in, we need user details and rights
|
||||||
|
@ -202,9 +188,7 @@ if ($page == 'logout') {
|
||||||
$server_endpoint = '/health';
|
$server_endpoint = '/health';
|
||||||
$server_status = $serverObject->getServerStatus($server_host, $server_port, $server_endpoint);
|
$server_status = $serverObject->getServerStatus($server_host, $server_port, $server_endpoint);
|
||||||
if (!$server_status) {
|
if (!$server_status) {
|
||||||
$error = 'The Jilo Server is not running. Some data may be old and incorrect.';
|
echo Messages::render('ERROR', 'DEFAULT', 'The Jilo Server is not running. Some data may be old and incorrect.', false);
|
||||||
Messages::get('SECURITY', 'RATE_LIMIT_INFO');
|
|
||||||
Messages::render('SECURITY', 'RATE_LIMIT_INFO');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +198,6 @@ if ($page == 'logout') {
|
||||||
if (isset($currentUser)) {
|
if (isset($currentUser)) {
|
||||||
include '../app/templates/page-sidebar.php';
|
include '../app/templates/page-sidebar.php';
|
||||||
}
|
}
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
if (in_array($page, $allowed_urls)) {
|
if (in_array($page, $allowed_urls)) {
|
||||||
// all normal pages
|
// all normal pages
|
||||||
include "../app/pages/{$page}.php";
|
include "../app/pages/{$page}.php";
|
||||||
|
|
Loading…
Reference in New Issue