Enhance user actions with CSRF protection and POST requests

Added CSRF protection and enforced POST requests for user delete and toggle status actions in UserController. Updated the users index view to use JavaScript for submitting POST forms with CSRF tokens for these actions, improving security and user experience. Also improved login success messages to include the user's full name.
This commit is contained in:
Hosteroid
2025-12-15 17:48:55 +02:00
parent bcd956a495
commit 1e98b8a047
4 changed files with 64 additions and 5 deletions

View File

@@ -167,6 +167,11 @@ class AuthController extends Controller
$_SESSION['email'] = $user['email']; $_SESSION['email'] = $user['email'];
$_SESSION['role'] = $user['role']; $_SESSION['role'] = $user['role'];
// Clear any existing session messages before successful login
unset($_SESSION['error']);
unset($_SESSION['success']);
unset($_SESSION['info']);
// Session is automatically tracked by DatabaseSessionHandler // Session is automatically tracked by DatabaseSessionHandler
// No need to manually create session record // No need to manually create session record
@@ -178,6 +183,9 @@ class AuthController extends Controller
// Update last login // Update last login
$this->userModel->updateLastLogin($user['id']); $this->userModel->updateLastLogin($user['id']);
// Set success message for login
$_SESSION['success'] = 'Login successful! Welcome back, ' . htmlspecialchars($user['full_name']) . '.';
// Redirect to dashboard // Redirect to dashboard
$this->redirect('/'); $this->redirect('/');
} }

View File

@@ -295,7 +295,7 @@ class TwoFactorController extends Controller
'method' => $method 'method' => $method
]); ]);
$_SESSION['success'] = 'Login successful!'; $_SESSION['success'] = 'Login successful! Welcome back, ' . htmlspecialchars($user['full_name']) . '.';
$this->redirect('/'); $this->redirect('/');
} else { } else {
$_SESSION['error'] = 'Invalid verification code. Please try again.'; $_SESSION['error'] = 'Invalid verification code. Please try again.';

View File

@@ -306,6 +306,14 @@ class UserController extends Controller
*/ */
public function delete($params = []) public function delete($params = [])
{ {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$this->redirect('/users');
return;
}
// CSRF Protection
$this->verifyCsrf('/users');
$userId = $params['id'] ?? 0; $userId = $params['id'] ?? 0;
$user = $this->userModel->find($userId); $user = $this->userModel->find($userId);
@@ -348,6 +356,14 @@ class UserController extends Controller
*/ */
public function toggleStatus($params = []) public function toggleStatus($params = [])
{ {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$this->redirect('/users');
return;
}
// CSRF Protection
$this->verifyCsrf('/users');
$userId = $params['id'] ?? 0; $userId = $params['id'] ?? 0;
$user = $this->userModel->find($userId); $user = $this->userModel->find($userId);

View File

@@ -273,15 +273,16 @@ $pagination = $pagination ?? [
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</a> </a>
<?php if ($user['id'] != \Core\Auth::id()): ?> <?php if ($user['id'] != \Core\Auth::id()): ?>
<a href="/users/<?= $user['id'] ?>/toggle-status" <a href="#"
class="text-orange-600 hover:text-orange-800" class="text-orange-600 hover:text-orange-800"
title="<?= $user['is_active'] ? 'Deactivate' : 'Activate' ?>"> title="<?= $user['is_active'] ? 'Deactivate' : 'Activate' ?>"
onclick="toggleUserStatus(<?= $user['id'] ?>); return false;">
<i class="fas fa-<?= $user['is_active'] ? 'user-slash' : 'user-check' ?>"></i> <i class="fas fa-<?= $user['is_active'] ? 'user-slash' : 'user-check' ?>"></i>
</a> </a>
<a href="/users/<?= $user['id'] ?>/delete" <a href="#"
class="text-red-600 hover:text-red-800" class="text-red-600 hover:text-red-800"
title="Delete" title="Delete"
onclick="return confirm('Are you sure you want to delete this user?')"> onclick="deleteUser(<?= $user['id'] ?>); return false;">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</a> </a>
<?php else: ?> <?php else: ?>
@@ -479,6 +480,40 @@ function bulkToggleStatus(action) {
form.submit(); form.submit();
} }
function toggleUserStatus(userId) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '/users/' + userId + '/toggle-status';
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrf_token';
csrfInput.value = '<?= csrf_token() ?>';
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
}
function deleteUser(userId) {
if (!confirm('Are you sure you want to delete this user? This action cannot be undone.')) {
return;
}
const form = document.createElement('form');
form.method = 'POST';
form.action = '/users/' + userId + '/delete';
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrf_token';
csrfInput.value = '<?= csrf_token() ?>';
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
}
function bulkDeleteUsers() { function bulkDeleteUsers() {
const userIds = getSelectedUserIds(); const userIds = getSelectedUserIds();