fix: Delete groups/servers using fetch with CSRF header
- Create CsrfHeaderMiddleware to read X-CSRF-TOKEN header - Replace form submit with JavaScript fetch for DELETE operations - Send CSRF token in X-CSRF-TOKEN header - Fix groups/index.twig and servers/index.twig delete buttons
This commit is contained in:
parent
1ab4bcd697
commit
d5318f7e16
|
|
@ -12,6 +12,7 @@ use App\Controllers\ServerDetailController;
|
|||
use App\Controllers\DashboardController;
|
||||
use App\Middlewares\AuthMiddleware;
|
||||
use App\Middlewares\SessionMiddleware;
|
||||
use App\Middlewares\CsrfHeaderMiddleware;
|
||||
use App\Models\User;
|
||||
use App\Models\Server as ServerModel;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
|
|
@ -195,7 +196,7 @@ $groupsGroup = $app->group('/groups', function ($group) use ($groupController) {
|
|||
$group->post('/{id}', [$groupController, 'update']);
|
||||
$group->delete('/{id}', [$groupController, 'delete']);
|
||||
$group->get('/{id}', [$groupController, 'show']);
|
||||
})->add($csrfMiddleware)->add(AuthMiddleware::class);
|
||||
})->add($csrfMiddleware)->add(new CsrfHeaderMiddleware())->add(AuthMiddleware::class);
|
||||
|
||||
// Redirect old /server/{id} to /servers/{id}
|
||||
$app->get("/server/{id}", function ($request, $response, $args) {
|
||||
|
|
@ -213,7 +214,7 @@ $serversGroup = $app->group('/servers', function ($group) use ($serverController
|
|||
$group->get('/{id}/regenerate-token', [$serverController, 'regenerateToken']);
|
||||
$group->post('/{id}/thresholds', [$serverDetailController, 'saveThresholds']);
|
||||
$group->post('/{id}/services', [$serverDetailController, 'saveServices']);
|
||||
})->add($csrfMiddleware)->add(AuthMiddleware::class);
|
||||
})->add($csrfMiddleware)->add(new CsrfHeaderMiddleware())->add(AuthMiddleware::class);
|
||||
|
||||
// Server detail route (protected with auth middleware and csrf)
|
||||
$app->get('/servers/{id}', [$serverDetailController, 'show'])->add(AuthMiddleware::class);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
// src/Middlewares/CsrfHeaderMiddleware.php
|
||||
|
||||
namespace App\Middlewares;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||
|
||||
class CsrfHeaderMiddleware
|
||||
{
|
||||
public function __invoke(Request $request, RequestHandler $handler): Response
|
||||
{
|
||||
$token = $request->getHeaderLine('X-CSRF-TOKEN');
|
||||
|
||||
if ($token) {
|
||||
$parsedBody = $request->getParsedBody() ?? [];
|
||||
$parsedBody['csrf_value'] = $token;
|
||||
$request = $request->withParsedBody($parsedBody);
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,33 @@
|
|||
{% extends "layout.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<script>
|
||||
function deleteGroup(id) {
|
||||
if (!confirm('Вы уверены, что хотите удалить эту группу?')) return;
|
||||
|
||||
var csrfName = document.querySelector('input[name="{{ csrf.name_key }}"]');
|
||||
var csrfValue = document.querySelector('input[name="{{ csrf.value_key }}"]');
|
||||
|
||||
fetch('/groups/' + id, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfValue ? csrfValue.value : '',
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Ошибка при удалении группы');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
alert('Ошибка: ' + err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
|
|
@ -44,14 +71,9 @@
|
|||
<a href="/groups/{{ group.id }}/edit" class="btn btn-sm btn-outline-primary me-1">
|
||||
<i class="fas fa-edit"></i> <span class="d-none d-sm-inline">Редактировать</span>
|
||||
</a>
|
||||
<form action="/groups/{{ group.id }}" method="post" style="display: inline-block;" onsubmit="return confirm('Вы уверены, что хотите удалить эту группу?');">
|
||||
<input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}">
|
||||
<input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}">
|
||||
<input type="hidden" name="_METHOD" value="DELETE">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteGroup({{ group.id }})">
|
||||
<i class="fas fa-trash"></i> <span class="d-none d-sm-inline">Удалить</span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,32 @@
|
|||
{% extends "layout.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<script>
|
||||
function deleteServer(id) {
|
||||
if (!confirm('Вы уверены, что хотите удалить этот сервер?')) return;
|
||||
|
||||
var csrfValue = document.querySelector('input[name="{{ csrf.value_key }}"]');
|
||||
|
||||
fetch('/servers/' + id, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfValue ? csrfValue.value : '',
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Ошибка при удалении сервера');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
alert('Ошибка: ' + err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
|
|
@ -52,14 +78,9 @@
|
|||
<a href="/servers/{{ server.id }}/edit" class="btn btn-sm btn-outline-primary" title="Редактировать">
|
||||
<i class="fas fa-edit"></i> <span class="d-none d-sm-inline">Редактировать</span>
|
||||
</a>
|
||||
<form action="/servers/{{ server.id }}" method="post" style="display: inline-block;" onsubmit="return confirm('Вы уверены, что хотите удалить этот сервер?');">
|
||||
<input type="hidden" name="{{ csrf.name_key }}" value="{{ csrf.name }}">
|
||||
<input type="hidden" name="{{ csrf.value_key }}" value="{{ csrf.value }}">
|
||||
<input type="hidden" name="_METHOD" value="DELETE">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Удалить">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" title="Удалить" onclick="deleteServer({{ server.id }})">
|
||||
<i class="fas fa-trash"></i> <span class="d-none d-sm-inline">Удалить</span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue