diff --git a/src/Controller/Backoffice/PrepareEliminationController.php b/src/Controller/Backoffice/PrepareEliminationController.php index f5a979c..4627372 100644 --- a/src/Controller/Backoffice/PrepareEliminationController.php +++ b/src/Controller/Backoffice/PrepareEliminationController.php @@ -10,6 +10,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Requirement\Requirement; +use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid; use Tvdt\Controller\AbstractController; use Tvdt\Entity\Elimination; use Tvdt\Entity\Quiz; @@ -20,35 +21,30 @@ final class PrepareEliminationController extends AbstractController { public function __construct(private readonly EliminationFactory $eliminationFactory, private readonly EntityManagerInterface $em) {} + #[IsCsrfTokenValid('prepare_elimination')] #[Route( '/backoffice/season/{seasonCode:season}/quiz/{quiz}/elimination/prepare', name: 'tvdt_prepare_elimination', requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID], methods: ['POST'], )] - public function index(Season $season, Quiz $quiz, Request $request): RedirectResponse + public function index(Season $season, Quiz $quiz): RedirectResponse { - if (!$this->isCsrfTokenValid('prepare_elimination', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - $elimination = $this->eliminationFactory->createEliminationFromQuiz($quiz); return $this->redirectToRoute('tvdt_prepare_elimination_view', ['elimination' => $elimination->id]); } + #[IsCsrfTokenValid('prepare_elimination', methods: ['POST'])] #[Route( '/backoffice/elimination/{elimination}', name: 'tvdt_prepare_elimination_view', requirements: ['elimination' => Requirement::UUID], + methods: ['GET', 'POST'], )] public function viewElimination(Elimination $elimination, Request $request): Response { - if ('POST' === $request->getMethod()) { - if (!$this->isCsrfTokenValid('prepare_elimination', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - + if ($request->isMethod('POST')) { $elimination->updateFromInputBag($request->request); $this->em->flush(); @@ -57,6 +53,8 @@ final class PrepareEliminationController extends AbstractController } $this->addFlash('success', 'Elimination updated'); + + return $this->redirectToRoute('tvdt_prepare_elimination_view', ['elimination' => $elimination->id]); } return $this->render('backoffice/prepare_elimination/index.html.twig', [ diff --git a/src/Controller/Backoffice/QuizController.php b/src/Controller/Backoffice/QuizController.php index a8266ea..c12ffae 100644 --- a/src/Controller/Backoffice/QuizController.php +++ b/src/Controller/Backoffice/QuizController.php @@ -12,6 +12,7 @@ use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Requirement\Requirement; +use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid; use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Contracts\Translation\TranslatorInterface; use Tvdt\Controller\AbstractController; @@ -183,6 +184,7 @@ class QuizController extends AbstractController ]); } + #[IsCsrfTokenValid('candidate_answer')] #[IsGranted(SeasonVoter::EDIT, subject: 'season')] #[Route( '/backoffice/season/{seasonCode:season}/quiz/{quiz}/candidates/{question}', @@ -192,10 +194,6 @@ class QuizController extends AbstractController )] public function saveCandidateAnswers(Season $season, Quiz $quiz, Question $question, Request $request): RedirectResponse { - if (!$this->isCsrfTokenValid('candidate_answer', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - if (false === $season->quizzes->contains($quiz) || false === $quiz->questions->contains($question)) { throw new BadRequestHttpException('Invalid quiz or question'); @@ -244,6 +242,7 @@ class QuizController extends AbstractController ]); } + #[IsCsrfTokenValid('enable_quiz')] #[IsGranted(SeasonVoter::EDIT, subject: 'season')] #[Route( '/backoffice/season/{seasonCode:season}/quiz/{quiz}/enable', @@ -251,12 +250,8 @@ class QuizController extends AbstractController requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'quiz' => Requirement::UUID.'|null'], methods: ['POST'], )] - public function enableQuiz(Season $season, ?Quiz $quiz, Request $request): RedirectResponse + public function enableQuiz(Season $season, ?Quiz $quiz): RedirectResponse { - if (!$this->isCsrfTokenValid('enable_quiz', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - $season->activeQuiz = $quiz; $this->em->flush(); @@ -267,6 +262,7 @@ class QuizController extends AbstractController return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $season->seasonCode]); } + #[IsCsrfTokenValid('clear_quiz')] #[IsGranted(SeasonVoter::EDIT, subject: 'quiz')] #[Route( '/backoffice/quiz/{quiz}/clear', @@ -274,12 +270,8 @@ class QuizController extends AbstractController requirements: ['quiz' => Requirement::UUID], methods: ['POST'], )] - public function clearQuiz(Quiz $quiz, Request $request): RedirectResponse + public function clearQuiz(Quiz $quiz): RedirectResponse { - if (!$this->isCsrfTokenValid('clear_quiz', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - try { $this->quizRepository->clearQuiz($quiz); $this->addFlash('success', $this->translator->trans('Quiz cleared')); @@ -290,6 +282,7 @@ class QuizController extends AbstractController return $this->redirectToRoute('tvdt_backoffice_quiz', ['seasonCode' => $quiz->season->seasonCode, 'quiz' => $quiz->id]); } + #[IsCsrfTokenValid('delete_quiz')] #[IsGranted(SeasonVoter::DELETE, subject: 'quiz')] #[Route( '/backoffice/quiz/{quiz}/delete', @@ -297,12 +290,8 @@ class QuizController extends AbstractController requirements: ['quiz' => Requirement::UUID], methods: ['POST'], )] - public function deleteQuiz(Quiz $quiz, Request $request): RedirectResponse + public function deleteQuiz(Quiz $quiz): RedirectResponse { - if (!$this->isCsrfTokenValid('delete_quiz', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - $this->quizRepository->deleteQuiz($quiz); $this->addFlash('success', $this->translator->trans('Quiz deleted')); @@ -310,6 +299,7 @@ class QuizController extends AbstractController return $this->redirectToRoute('tvdt_backoffice_season', ['seasonCode' => $quiz->season->seasonCode]); } + #[IsCsrfTokenValid('candidate_correction')] #[IsGranted(SeasonVoter::EDIT, subject: 'quiz')] #[Route( '/backoffice/quiz/{quiz}/candidate/{candidate}/modify_correction', @@ -319,10 +309,6 @@ class QuizController extends AbstractController )] public function modifyCorrection(Quiz $quiz, Candidate $candidate, Request $request): RedirectResponse { - if (!$this->isCsrfTokenValid('candidate_correction', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - $corrections = (float) $request->request->get('corrections'); $this->quizCandidateRepository->setCorrectionsForCandidate($quiz, $candidate, $corrections); @@ -330,6 +316,7 @@ class QuizController extends AbstractController return $this->redirectToRoute('tvdt_backoffice_quiz', ['seasonCode' => $quiz->season->seasonCode, 'quiz' => $quiz->id]); } + #[IsCsrfTokenValid('candidate_penalty')] #[IsGranted(SeasonVoter::EDIT, subject: 'quiz')] #[Route( '/backoffice/quiz/{quiz}/candidate/{candidate}/modify_penalty', @@ -339,10 +326,6 @@ class QuizController extends AbstractController )] public function modifyPenalty(Quiz $quiz, Candidate $candidate, Request $request): RedirectResponse { - if (!$this->isCsrfTokenValid('candidate_penalty', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - $penalty = (int) $request->request->get('penalty'); $this->quizCandidateRepository->setPenaltyForCandidate($quiz, $candidate, $penalty); @@ -350,6 +333,7 @@ class QuizController extends AbstractController return $this->redirectToRoute('tvdt_backoffice_quiz', ['seasonCode' => $quiz->season->seasonCode, 'quiz' => $quiz->id]); } + #[IsCsrfTokenValid('toggle_candidate')] #[IsGranted(SeasonVoter::EDIT, subject: 'quiz')] #[Route( '/backoffice/quiz/{quiz}/candidate/{candidate}/toggle', @@ -357,12 +341,8 @@ class QuizController extends AbstractController requirements: ['quiz' => Requirement::UUID, 'candidate' => Requirement::UUID], methods: ['POST'], )] - public function toggleCandidate(Quiz $quiz, Candidate $candidate, Request $request): RedirectResponse + public function toggleCandidate(Quiz $quiz, Candidate $candidate): RedirectResponse { - if (!$this->isCsrfTokenValid('toggle_candidate', $request->request->get('_token'))) { - throw $this->createAccessDeniedException(); - } - $quizCandidate = $this->quizCandidateRepository->findOneBy([ 'quiz' => $quiz, 'candidate' => $candidate, diff --git a/src/Controller/QuizController.php b/src/Controller/QuizController.php index 26f53d5..fcbb8a6 100644 --- a/src/Controller/QuizController.php +++ b/src/Controller/QuizController.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid; use Symfony\Contracts\Translation\TranslatorInterface; use Tvdt\Entity\Answer; use Tvdt\Entity\Candidate; @@ -70,10 +71,12 @@ final class QuizController extends AbstractController return $this->render('quiz/enter_name.twig', ['season' => $season, 'form' => $form]); } + #[IsCsrfTokenValid('question', tokenKey: 'token', methods: ['POST'])] #[Route( path: '/{seasonCode:season}/{nameHash}', name: 'tvdt_quiz_quiz_page', requirements: ['seasonCode' => self::SEASON_CODE_REGEX, 'nameHash' => self::CANDIDATE_HASH_REGEX], + methods: ['GET', 'POST'], )] public function quizPage( Season $season, @@ -96,11 +99,7 @@ final class QuizController extends AbstractController return $this->redirectToRoute('tvdt_quiz_enter_name', ['seasonCode' => $season->seasonCode]); } - if ('POST' === $request->getMethod()) { - if (!$this->isCsrfTokenValid('question', $request->request->get('token'))) { - throw $this->createAccessDeniedException(); - } - + if ($request->isMethod('POST')) { // TODO: Extract saving answer logic to a service // Check if candidate is inactive for this quiz $quizCandidate = $this->quizCandidateRepository->findOneBy(['quiz' => $quiz, 'candidate' => $candidate]);