Adds new message system with dismissible messages
parent
eae2a8a47c
commit
608946ddee
|
@ -0,0 +1,137 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Messages {
|
||||||
|
// Message types
|
||||||
|
const TYPE_SUCCESS = 'success';
|
||||||
|
const TYPE_ERROR = 'danger';
|
||||||
|
const TYPE_INFO = 'info';
|
||||||
|
const TYPE_WARNING = 'warning';
|
||||||
|
|
||||||
|
// Message categories
|
||||||
|
const SECURITY = [
|
||||||
|
'WHITELIST_ADD_SUCCESS' => [
|
||||||
|
'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,
|
||||||
|
'dismissible' => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
const LOGIN = [
|
||||||
|
'LOGIN_FAILED' => [
|
||||||
|
'message' => 'Invalid username or password.',
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => true
|
||||||
|
],
|
||||||
|
'LOGIN_BLOCKED' => [
|
||||||
|
'message' => 'Too many failed attempts. Please try again later.',
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => false
|
||||||
|
],
|
||||||
|
'IP_BLACKLISTED' => [
|
||||||
|
'message' => 'Access denied. Your IP address is blacklisted.',
|
||||||
|
'type' => self::TYPE_ERROR,
|
||||||
|
'dismissible' => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get message configuration by key
|
||||||
|
*/
|
||||||
|
public static function get($category, $key) {
|
||||||
|
$messages = constant("self::$category");
|
||||||
|
return $messages[$key] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render message HTML
|
||||||
|
*/
|
||||||
|
public static function render($category, $key, $customMessage = null) {
|
||||||
|
$config = self::get($category, $key);
|
||||||
|
if (!$config) return '';
|
||||||
|
|
||||||
|
$message = $customMessage ?? $config['message'];
|
||||||
|
$dismissible = $config['dismissible'] ? ' alert-dismissible fade show' : '';
|
||||||
|
$dismissButton = $config['dismissible'] ? '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' : '';
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'<div class="alert alert-%s%s" role="alert">%s%s</div>',
|
||||||
|
$config['type'],
|
||||||
|
$dismissible,
|
||||||
|
htmlspecialchars($message),
|
||||||
|
$dismissButton
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store message in session for display after redirect
|
||||||
|
*/
|
||||||
|
public static function flash($category, $key, $customMessage = null) {
|
||||||
|
if (!isset($_SESSION['flash_messages'])) {
|
||||||
|
$_SESSION['flash_messages'] = [];
|
||||||
|
}
|
||||||
|
$_SESSION['flash_messages'][] = [
|
||||||
|
'category' => $category,
|
||||||
|
'key' => $key,
|
||||||
|
'custom_message' => $customMessage
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get and clear all flash messages
|
||||||
|
*/
|
||||||
|
public static function getFlash() {
|
||||||
|
$messages = $_SESSION['flash_messages'] ?? [];
|
||||||
|
unset($_SESSION['flash_messages']);
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,96 +9,106 @@ if (!($userObject->hasRight($user_id, 'superuser') ||
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include Messages class
|
||||||
|
require_once '../app/classes/messages.php';
|
||||||
|
|
||||||
// Initialize variables for feedback messages
|
// Initialize variables for feedback messages
|
||||||
$error_message = '';
|
$messages = [];
|
||||||
$success_message = '';
|
|
||||||
|
// Get current section
|
||||||
|
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
||||||
|
|
||||||
// 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'];
|
||||||
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'add_whitelist':
|
case 'add_whitelist':
|
||||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit whitelist')) {
|
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit whitelist')) {
|
||||||
throw new Exception('You do not have permission to modify the whitelist.');
|
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||||
}
|
}
|
||||||
if (empty($_POST['ip_address'])) {
|
if (empty($_POST['ip_address'])) {
|
||||||
throw new Exception('IP address is required.');
|
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||||
}
|
}
|
||||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||||
if (!$rateLimiter->addToWhitelist($_POST['ip_address'], $is_network, $_POST['description'] ?? '', $currentUser, $user_id)) {
|
if (!$rateLimiter->addToWhitelist($_POST['ip_address'], $is_network, $_POST['description'] ?? '', $currentUser, $user_id)) {
|
||||||
throw new Exception('Failed to add IP to whitelist. Please check the IP format.');
|
throw new Exception(Messages::get('SECURITY', 'WHITELIST_ADD_ERROR')['message']);
|
||||||
}
|
}
|
||||||
$success_message = 'IP address successfully added to whitelist.';
|
Messages::flash('SECURITY', 'WHITELIST_ADD_SUCCESS');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'remove_whitelist':
|
case 'remove_whitelist':
|
||||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit whitelist')) {
|
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit whitelist')) {
|
||||||
throw new Exception('You do not have permission to modify the whitelist.');
|
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||||
}
|
}
|
||||||
if (empty($_POST['ip_address'])) {
|
if (empty($_POST['ip_address'])) {
|
||||||
throw new Exception('IP address is required.');
|
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||||
}
|
}
|
||||||
if (!$rateLimiter->removeFromWhitelist($_POST['ip_address'], $currentUser, $user_id)) {
|
if (!$rateLimiter->removeFromWhitelist($_POST['ip_address'], $currentUser, $user_id)) {
|
||||||
throw new Exception('Failed to remove IP from whitelist.');
|
throw new Exception(Messages::get('SECURITY', 'WHITELIST_REMOVE_ERROR')['message']);
|
||||||
}
|
}
|
||||||
$success_message = 'IP address successfully removed from whitelist.';
|
Messages::flash('SECURITY', 'WHITELIST_REMOVE_SUCCESS');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'add_blacklist':
|
case 'add_blacklist':
|
||||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit blacklist')) {
|
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit blacklist')) {
|
||||||
throw new Exception('You do not have permission to modify the blacklist.');
|
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||||
}
|
}
|
||||||
if (empty($_POST['ip_address'])) {
|
if (empty($_POST['ip_address'])) {
|
||||||
throw new Exception('IP address is required.');
|
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||||
}
|
}
|
||||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||||
$expiry_hours = !empty($_POST['expiry_hours']) ? intval($_POST['expiry_hours']) : null;
|
$expiry_hours = !empty($_POST['expiry_hours']) ? intval($_POST['expiry_hours']) : null;
|
||||||
if (!$rateLimiter->addToBlacklist($_POST['ip_address'], $is_network, $_POST['reason'] ?? '', $currentUser, $user_id, $expiry_hours)) {
|
if (!$rateLimiter->addToBlacklist($_POST['ip_address'], $is_network, $_POST['reason'] ?? '', $currentUser, $user_id, $expiry_hours)) {
|
||||||
throw new Exception('Failed to add IP to blacklist. Please check the IP format.');
|
throw new Exception(Messages::get('SECURITY', 'BLACKLIST_ADD_ERROR')['message']);
|
||||||
}
|
}
|
||||||
$success_message = 'IP address successfully added to blacklist.';
|
Messages::flash('SECURITY', 'BLACKLIST_ADD_SUCCESS');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'remove_blacklist':
|
case 'remove_blacklist':
|
||||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit blacklist')) {
|
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit blacklist')) {
|
||||||
throw new Exception('You do not have permission to modify the blacklist.');
|
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||||
}
|
}
|
||||||
if (empty($_POST['ip_address'])) {
|
if (empty($_POST['ip_address'])) {
|
||||||
throw new Exception('IP address is required.');
|
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||||
}
|
}
|
||||||
if (!$rateLimiter->removeFromBlacklist($_POST['ip_address'], $currentUser, $user_id)) {
|
if (!$rateLimiter->removeFromBlacklist($_POST['ip_address'], $currentUser, $user_id)) {
|
||||||
throw new Exception('Failed to remove IP from blacklist.');
|
throw new Exception(Messages::get('SECURITY', 'BLACKLIST_REMOVE_ERROR')['message']);
|
||||||
}
|
}
|
||||||
$success_message = 'IP address successfully removed from blacklist.';
|
Messages::flash('SECURITY', 'BLACKLIST_REMOVE_SUCCESS');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error_message = $e->getMessage();
|
$messages[] = ['category' => 'SECURITY', 'key' => 'CUSTOM_ERROR', 'custom_message' => $e->getMessage()];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($error_message)) {
|
if (empty($messages)) {
|
||||||
// Only redirect if there was no error
|
// Only redirect if there were no errors
|
||||||
header("Location: {$app_root}?page=security§ion={$section}" .
|
header("Location: {$app_root}?page=security§ion={$section}");
|
||||||
($success_message ? '&success=' . urlencode($success_message) : ''));
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get success message from URL if redirected after successful action
|
// Get flash messages from previous request
|
||||||
if (isset($_GET['success'])) {
|
$flash_messages = Messages::getFlash();
|
||||||
$success_message = $_GET['success'];
|
$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
|
||||||
|
if ($section === 'ratelimit') {
|
||||||
|
$messages[] = ['category' => 'SECURITY', 'key' => 'RATE_LIMIT_INFO'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current lists
|
// Get current lists
|
||||||
$whitelisted = $rateLimiter->getWhitelistedIps();
|
$whitelisted = $rateLimiter->getWhitelistedIps();
|
||||||
$blacklisted = $rateLimiter->getBlacklistedIps();
|
$blacklisted = $rateLimiter->getBlacklistedIps();
|
||||||
|
|
||||||
// Get current section
|
|
||||||
$section = isset($_GET['section']) ? $_GET['section'] : 'whitelist';
|
|
||||||
|
|
||||||
// Include template
|
// Include template
|
||||||
include '../app/templates/security.php';
|
include '../app/templates/security.php';
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,9 @@
|
||||||
<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 if (!empty($error_message)): ?>
|
<?php foreach ($messages as $msg): ?>
|
||||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
<?= Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null) ?>
|
||||||
<?= htmlspecialchars($error_message) ?>
|
<?php endforeach; ?>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (!empty($success_message)): ?>
|
|
||||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
|
||||||
<?= htmlspecialchars($success_message) ?>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<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">
|
||||||
|
|
Loading…
Reference in New Issue