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;
|
||||
}
|
||||
|
||||
// Include Messages class
|
||||
require_once '../app/classes/messages.php';
|
||||
|
||||
// Initialize variables for feedback messages
|
||||
$error_message = '';
|
||||
$success_message = '';
|
||||
$messages = [];
|
||||
|
||||
// Get current section
|
||||
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$action = $_POST['action'];
|
||||
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
case 'add_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'])) {
|
||||
throw new Exception('IP address is required.');
|
||||
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||
}
|
||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||
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;
|
||||
|
||||
case 'remove_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'])) {
|
||||
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)) {
|
||||
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;
|
||||
|
||||
case 'add_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'])) {
|
||||
throw new Exception('IP address is required.');
|
||||
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||
}
|
||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||
$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)) {
|
||||
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;
|
||||
|
||||
case 'remove_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'])) {
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error_message = $e->getMessage();
|
||||
$messages[] = ['category' => 'SECURITY', 'key' => 'CUSTOM_ERROR', 'custom_message' => $e->getMessage()];
|
||||
}
|
||||
|
||||
if (empty($error_message)) {
|
||||
// Only redirect if there was no error
|
||||
header("Location: {$app_root}?page=security§ion={$section}" .
|
||||
($success_message ? '&success=' . urlencode($success_message) : ''));
|
||||
if (empty($messages)) {
|
||||
// Only redirect if there were no errors
|
||||
header("Location: {$app_root}?page=security§ion={$section}");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Get success message from URL if redirected after successful action
|
||||
if (isset($_GET['success'])) {
|
||||
$success_message = $_GET['success'];
|
||||
// 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
|
||||
if ($section === 'ratelimit') {
|
||||
$messages[] = ['category' => 'SECURITY', 'key' => 'RATE_LIMIT_INFO'];
|
||||
}
|
||||
|
||||
// Get current lists
|
||||
$whitelisted = $rateLimiter->getWhitelistedIps();
|
||||
$blacklisted = $rateLimiter->getBlacklistedIps();
|
||||
|
||||
// Get current section
|
||||
$section = isset($_GET['section']) ? $_GET['section'] : 'whitelist';
|
||||
|
||||
// Include template
|
||||
include '../app/templates/security.php';
|
||||
|
||||
|
|
|
@ -3,18 +3,9 @@
|
|||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Security Settings</h2>
|
||||
<?php if (!empty($error_message)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= htmlspecialchars($error_message) ?>
|
||||
<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; ?>
|
||||
<?php foreach ($messages as $msg): ?>
|
||||
<?= Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null) ?>
|
||||
<?php endforeach; ?>
|
||||
<ul class="nav nav-tabs">
|
||||
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) { ?>
|
||||
<li class="nav-item">
|
||||
|
|
Loading…
Reference in New Issue