mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-07-06 07:30:17 +02:00
Add CSRF token validation for quiz-related actions
- Added CSRF validation to `enableQuiz`, `clearQuiz`, `deleteQuiz`, `toggleCandidate`, and `prepareElimination` actions. - Updated Twig templates to replace links with POST forms to include CSRF tokens. - Set HTTP method restrictions for related endpoints to `POST`.
This commit is contained in:
@@ -24,9 +24,14 @@ final class PrepareEliminationController extends AbstractController
|
|||||||
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/elimination/prepare',
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/elimination/prepare',
|
||||||
name: 'tvdt_prepare_elimination',
|
name: 'tvdt_prepare_elimination',
|
||||||
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID],
|
||||||
|
methods: ['POST'],
|
||||||
)]
|
)]
|
||||||
public function index(Season $season, Quiz $quiz): RedirectResponse
|
public function index(Season $season, Quiz $quiz, Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('prepare_elimination', $request->request->get('_token'))) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
$elimination = $this->eliminationFactory->createEliminationFromQuiz($quiz);
|
$elimination = $this->eliminationFactory->createEliminationFromQuiz($quiz);
|
||||||
|
|
||||||
return $this->redirectToRoute('tvdt_prepare_elimination_view', ['elimination' => $elimination->id]);
|
return $this->redirectToRoute('tvdt_prepare_elimination_view', ['elimination' => $elimination->id]);
|
||||||
|
|||||||
@@ -250,9 +250,14 @@ class QuizController extends AbstractController
|
|||||||
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/enable',
|
'/backoffice/season/{seasonCode:season}/quiz/{quiz}/enable',
|
||||||
name: 'tvdt_backoffice_enable',
|
name: 'tvdt_backoffice_enable',
|
||||||
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID.'|null'],
|
requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID.'|null'],
|
||||||
|
methods: ['POST'],
|
||||||
)]
|
)]
|
||||||
public function enableQuiz(Season $season, ?Quiz $quiz): RedirectResponse
|
public function enableQuiz(Season $season, ?Quiz $quiz, Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('enable_quiz', $request->request->get('_token'))) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
$season->activeQuiz = $quiz;
|
$season->activeQuiz = $quiz;
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
|
||||||
@@ -268,9 +273,14 @@ class QuizController extends AbstractController
|
|||||||
'/backoffice/quiz/{quiz}/clear',
|
'/backoffice/quiz/{quiz}/clear',
|
||||||
name: 'tvdt_backoffice_quiz_clear',
|
name: 'tvdt_backoffice_quiz_clear',
|
||||||
requirements: ['quiz' => Requirement::UUID],
|
requirements: ['quiz' => Requirement::UUID],
|
||||||
|
methods: ['POST'],
|
||||||
)]
|
)]
|
||||||
public function clearQuiz(Quiz $quiz): RedirectResponse
|
public function clearQuiz(Quiz $quiz, Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('clear_quiz', $request->request->get('_token'))) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->quizRepository->clearQuiz($quiz);
|
$this->quizRepository->clearQuiz($quiz);
|
||||||
$this->addFlash('success', $this->translator->trans('Quiz cleared'));
|
$this->addFlash('success', $this->translator->trans('Quiz cleared'));
|
||||||
@@ -286,9 +296,14 @@ class QuizController extends AbstractController
|
|||||||
'/backoffice/quiz/{quiz}/delete',
|
'/backoffice/quiz/{quiz}/delete',
|
||||||
name: 'tvdt_backoffice_quiz_delete',
|
name: 'tvdt_backoffice_quiz_delete',
|
||||||
requirements: ['quiz' => Requirement::UUID],
|
requirements: ['quiz' => Requirement::UUID],
|
||||||
|
methods: ['POST'],
|
||||||
)]
|
)]
|
||||||
public function deleteQuiz(Quiz $quiz): RedirectResponse
|
public function deleteQuiz(Quiz $quiz, Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('delete_quiz', $request->request->get('_token'))) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
$this->quizRepository->deleteQuiz($quiz);
|
$this->quizRepository->deleteQuiz($quiz);
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator->trans('Quiz deleted'));
|
$this->addFlash('success', $this->translator->trans('Quiz deleted'));
|
||||||
@@ -347,10 +362,14 @@ class QuizController extends AbstractController
|
|||||||
'/backoffice/quiz/{quiz}/candidate/{candidate}/toggle',
|
'/backoffice/quiz/{quiz}/candidate/{candidate}/toggle',
|
||||||
name: 'tvdt_backoffice_toggle_candidate',
|
name: 'tvdt_backoffice_toggle_candidate',
|
||||||
requirements: ['quiz' => Requirement::UUID, 'candidate' => Requirement::UUID],
|
requirements: ['quiz' => Requirement::UUID, 'candidate' => Requirement::UUID],
|
||||||
methods: ['GET'],
|
methods: ['POST'],
|
||||||
)]
|
)]
|
||||||
public function toggleCandidate(Quiz $quiz, Candidate $candidate): RedirectResponse
|
public function toggleCandidate(Quiz $quiz, Candidate $candidate, Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('toggle_candidate', $request->request->get('_token'))) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
$quizCandidate = $this->quizCandidateRepository->findOneBy([
|
$quizCandidate = $this->quizCandidateRepository->findOneBy([
|
||||||
'quiz' => $quiz,
|
'quiz' => $quiz,
|
||||||
'candidate' => $candidate,
|
'candidate' => $candidate,
|
||||||
|
|||||||
@@ -35,14 +35,16 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ path('tvdt_backoffice_toggle_candidate', {quiz: quiz.id, candidate: candidate.id}) }}"
|
<form action="{{ path('tvdt_backoffice_toggle_candidate', {quiz: quiz.id, candidate: candidate.id}) }}" method="POST">
|
||||||
class="btn btn-sm btn-outline-secondary">
|
<input type="hidden" name="_token" value="{{ csrf_token('toggle_candidate') }}">
|
||||||
|
<button type="submit" class="btn btn-sm btn-outline-secondary">
|
||||||
{% if quizCandidate == null or quizCandidate.active %}
|
{% if quizCandidate == null or quizCandidate.active %}
|
||||||
{{ 'Deactivate'|trans }}
|
{{ 'Deactivate'|trans }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ 'Activate'|trans }}
|
{{ 'Activate'|trans }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</button>
|
||||||
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -3,16 +3,24 @@
|
|||||||
<div class="mb-3 btn-group">
|
<div class="mb-3 btn-group">
|
||||||
|
|
||||||
{% if quiz is same as (season.activeQuiz) %}
|
{% if quiz is same as (season.activeQuiz) %}
|
||||||
<a class="btn btn-secondary"
|
<form action="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: 'null'}) }}" method="POST">
|
||||||
href="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: 'null'}) }}">{{ 'Deactivate Quiz'|trans }}</a>
|
<input type="hidden" name="_token" value="{{ csrf_token('enable_quiz') }}">
|
||||||
|
<button type="submit" class="btn btn-secondary rounded-0 rounded-start">
|
||||||
|
{{ 'Deactivate Quiz'|trans }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-primary"
|
<form action="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: quiz.id}) }}" method="POST">
|
||||||
href="{{ path('tvdt_backoffice_enable', {seasonCode: season.seasonCode, quiz: quiz.id}) }}">{{ 'Make active'|trans }}</a>
|
<input type="hidden" name="_token" value="{{ csrf_token('enable_quiz') }}">
|
||||||
|
<button type="submit" class="btn btn-primary rounded-0 rounded-start">
|
||||||
|
{{ 'Make active'|trans }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btn btn-danger" data-action="click->bo--quiz#clearQuiz">
|
<button class="btn btn-danger" data-action="click->bo--quiz#clearQuiz">
|
||||||
{{ 'Clear Quiz...'|trans }}
|
{{ 'Clear Quiz...'|trans }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" data-action="click->bo--quiz#deleteQuiz">
|
<button class="btn btn-danger rounded-0 rounded-end" data-action="click->bo--quiz#deleteQuiz">
|
||||||
{{ 'Delete Quiz...'|trans }}
|
{{ 'Delete Quiz...'|trans }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,8 +82,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
||||||
<a href="{{ path('tvdt_backoffice_quiz_clear', {quiz: quiz.id}) }}"
|
<form action="{{ path('tvdt_backoffice_quiz_clear', {quiz: quiz.id}) }}" method="POST">
|
||||||
class="btn btn-danger">{{ 'Yes'|trans }}</a>
|
<input type="hidden" name="_token" value="{{ csrf_token('clear_quiz') }}">
|
||||||
|
<button type="submit" class="btn btn-danger">{{ 'Yes'|trans }}</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,8 +107,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'No'|trans }}</button>
|
||||||
<a href="{{ path('tvdt_backoffice_quiz_delete', {quiz: quiz.id}) }}"
|
<form action="{{ path('tvdt_backoffice_quiz_delete', {quiz: quiz.id}) }}" method="POST">
|
||||||
class="btn btn-danger">{{ 'Yes'|trans }}</a>
|
<input type="hidden" name="_token" value="{{ csrf_token('delete_quiz') }}">
|
||||||
|
<button type="submit" class="btn btn-danger">{{ 'Yes'|trans }}</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
<div class="btn-toolbar mb-3" role="toolbar">
|
<div class="btn-toolbar mb-3" role="toolbar">
|
||||||
<div class="btn-group me-2">
|
<div class="btn-group me-2">
|
||||||
{# <a class="btn btn-primary">{{ 'Start Elimination'|trans }}</a> #}
|
{# <a class="btn btn-primary">{{ 'Start Elimination'|trans }}</a> #}
|
||||||
<a href="{{ path('tvdt_prepare_elimination', {seasonCode: season.seasonCode, quiz: quiz.id}) }}"
|
<form action="{{ path('tvdt_prepare_elimination', {seasonCode: season.seasonCode, quiz: quiz.id}) }}" method="POST">
|
||||||
class="btn btn-secondary">{{ 'Prepare Custom Elimination'|trans }}</a>
|
<input type="hidden" name="_token" value="{{ csrf_token('prepare_elimination') }}">
|
||||||
|
<button type="submit" class="btn btn-secondary rounded-0 rounded-start">{{ 'Prepare Custom Elimination'|trans }}</button>
|
||||||
|
</form>
|
||||||
{%~ if not quiz.eliminations.empty %}
|
{%~ if not quiz.eliminations.empty %}
|
||||||
<button class="btn btn-secondary dropdown-toggle"
|
<button class="btn btn-secondary dropdown-toggle"
|
||||||
data-bs-toggle="dropdown">{{ 'Load Prepared Elimination'|trans }}</button>
|
data-bs-toggle="dropdown">{{ 'Load Prepared Elimination'|trans }}</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user