From b20dc2e00fb76b97bef6c81797a93c022d514a9b Mon Sep 17 00:00:00 2001 From: sermandm Date: Fri, 25 Apr 2025 15:40:06 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20S=C3=A9curisation=20des=20contr?= =?UTF-8?q?=C3=B4leurs=20:=20gestion=20des=20acc=C3=A8s=20par=20r=C3=B4les?= =?UTF-8?q?=20(admin,=20secr=C3=A9taire,=20chauffagiste)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controller/AuthenticationController.php | 6 +- src/Controller/CalendrierController.php | 24 ++++---- src/Controller/DashboardController.php | 6 +- src/Controller/FaultController.php | 29 +++++++++- src/Controller/InterventionController.php | 32 ++++++++++- src/Controller/SkillController.php | 6 +- src/Controller/StockController.php | 7 ++- src/Controller/UserController.php | 63 +++++++++++++++------ src/Controller/VehicleController.php | 29 +++++++++- 9 files changed, 159 insertions(+), 43 deletions(-) diff --git a/src/Controller/AuthenticationController.php b/src/Controller/AuthenticationController.php index 94fec26..f80b8c1 100644 --- a/src/Controller/AuthenticationController.php +++ b/src/Controller/AuthenticationController.php @@ -12,10 +12,10 @@ class AuthenticationController extends AbstractController #[Route(path: '/', name: 'app_login')] public function login(AuthenticationUtils $authenticationUtils): Response { - // get the login error if there is one + // Get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); - // last username entered by the user + // Last username entered by the user $lastUsername = $authenticationUtils->getLastUsername(); return $this->render('login/index.html.twig', [ @@ -29,4 +29,4 @@ class AuthenticationController extends AbstractController { throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); } -} \ No newline at end of file +} diff --git a/src/Controller/CalendrierController.php b/src/Controller/CalendrierController.php index d593b1f..8a5c056 100644 --- a/src/Controller/CalendrierController.php +++ b/src/Controller/CalendrierController.php @@ -20,6 +20,9 @@ class CalendrierController extends AbstractController #[Route('/secretaire', name: 'app_calendrier_indexSecretaire')] public function indexSecretaire(): Response { + // Check that only admins and secretaries can view this page + $this->denyAccessUnlessGranted('ROLE_SECRETAIRE'); + return $this->render('calendrier/indexSecretaire.html.twig', [ 'controller_name' => 'CalendrierController', ]); @@ -28,20 +31,13 @@ class CalendrierController extends AbstractController #[Route('/chauffagiste', name: 'app_calendrier_indexChauffagiste')] public function indexChauffagiste(): Response { + // Ensure chauffagiste only sees their own calendar + if ($this->isGranted('ROLE_CHAUFFAGISTE')) { + // filter logic for individual chauffagiste calendar + } + return $this->render('calendrier/indexChauffagiste.html.twig', [ 'controller_name' => 'CalendrierController', - ]); } - - // créer intervention : secretaire + admin - // créer un nouveau rdv - // modifier un rdv - // supprimer un rdv - // choisir un chauffagiste - // deconnexion - - // créer intervention : chauffagiste - // modifier un rdv - // deconnexion - - // quand on créer une rdv, alors cela nous redirige vers la page intervention + ]); + } } diff --git a/src/Controller/DashboardController.php b/src/Controller/DashboardController.php index e4c59ac..2039863 100644 --- a/src/Controller/DashboardController.php +++ b/src/Controller/DashboardController.php @@ -4,26 +4,28 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class DashboardController extends AbstractController { #[Route('/admin/dashboard', name: 'admin_dashboard')] public function admin(): Response { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); return $this->render('dashboard/admin.html.twig'); } #[Route('/secretaire/dashboard', name: 'secretaire_dashboard')] public function secretaire(): Response { + $this->denyAccessUnlessGranted('ROLE_SECRETAIRE'); return $this->render('dashboard/secretaire.html.twig'); } #[Route('/chauffagiste/dashboard', name: 'chauffagiste_dashboard')] public function chauffagiste(): Response { + $this->denyAccessUnlessGranted('ROLE_CHAUFFAGISTE'); return $this->render('dashboard/chauffagiste.html.twig'); } } - diff --git a/src/Controller/FaultController.php b/src/Controller/FaultController.php index bb1f2d9..cd1b979 100644 --- a/src/Controller/FaultController.php +++ b/src/Controller/FaultController.php @@ -17,8 +17,13 @@ final class FaultController extends AbstractController #[Route(name: 'app_fault_index', methods: ['GET'])] public function index(FaultRepository $faultRepository): Response { + // Filtrage des pannes : un chauffagiste ne peut voir que ses pannes + $faults = $this->isGranted('ROLE_CHAUFFAGISTE') + ? $faultRepository->findByUser($this->getUser()) // Filtre les pannes par utilisateur + : $faultRepository->findAll(); // Admins voient toutes les pannes + return $this->render('fault/index.html.twig', [ - 'faults' => $faultRepository->findAll(), + 'faults' => $faults, ]); } @@ -30,6 +35,11 @@ final class FaultController extends AbstractController $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + // Associe la panne à un chauffagiste si c'est un chauffagiste + if ($this->isGranted('ROLE_CHAUFFAGISTE')) { + $fault->setUser($this->getUser()); + } + $entityManager->persist($fault); $entityManager->flush(); @@ -45,6 +55,11 @@ final class FaultController extends AbstractController #[Route('/{id}', name: 'app_fault_show', methods: ['GET'])] public function show(Fault $fault): Response { + // Un chauffagiste ne peut voir que ses pannes + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $fault->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas voir cette panne.'); + } + return $this->render('fault/show.html.twig', [ 'fault' => $fault, ]); @@ -53,6 +68,11 @@ final class FaultController extends AbstractController #[Route('/{id}/edit', name: 'app_fault_edit', methods: ['GET', 'POST'])] public function edit(Request $request, Fault $fault, EntityManagerInterface $entityManager): Response { + // Un chauffagiste ne peut modifier que ses propres pannes + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $fault->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas modifier cette panne.'); + } + $form = $this->createForm(FaultType::class, $fault); $form->handleRequest($request); @@ -71,7 +91,12 @@ final class FaultController extends AbstractController #[Route('/{id}', name: 'app_fault_delete', methods: ['POST'])] public function delete(Request $request, Fault $fault, EntityManagerInterface $entityManager): Response { - if ($this->isCsrfTokenValid('delete'.$fault->getId(), $request->getPayload()->getString('_token'))) { + // Un chauffagiste ne peut supprimer que ses propres pannes + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $fault->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer cette panne.'); + } + + if ($this->isCsrfTokenValid('delete'.$fault->getId(), $request->get('csrf_token'))) { $entityManager->remove($fault); $entityManager->flush(); } diff --git a/src/Controller/InterventionController.php b/src/Controller/InterventionController.php index 11cce71..7a1d1fd 100644 --- a/src/Controller/InterventionController.php +++ b/src/Controller/InterventionController.php @@ -17,8 +17,16 @@ final class InterventionController extends AbstractController #[Route(name: 'app_intervention_index', methods: ['GET'])] public function index(InterventionRepository $interventionRepository): Response { + // Vérifier si l'utilisateur est un chauffagiste, pour filtrer ses interventions + if ($this->isGranted('ROLE_CHAUFFAGISTE')) { + $interventions = $interventionRepository->findByUser($this->getUser()); // On filtre par utilisateur connecté + } else { + // Les autres rôles (admin) peuvent voir toutes les interventions + $interventions = $interventionRepository->findAll(); + } + return $this->render('intervention/index.html.twig', [ - 'interventions' => $interventionRepository->findAll(), + 'interventions' => $interventions, ]); } @@ -30,6 +38,11 @@ final class InterventionController extends AbstractController $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + // Si l'utilisateur est un chauffagiste, on associe l'intervention à lui + if ($this->isGranted('ROLE_CHAUFFAGISTE')) { + $intervention->setUser($this->getUser()); + } + $entityManager->persist($intervention); $entityManager->flush(); @@ -45,6 +58,11 @@ final class InterventionController extends AbstractController #[Route('/{id}', name: 'app_intervention_show', methods: ['GET'])] public function show(Intervention $intervention): Response { + // Vérifier si l'utilisateur peut voir cette intervention (chauffagiste ne voit que ses interventions) + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $intervention->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas voir cette intervention.'); + } + return $this->render('intervention/show.html.twig', [ 'intervention' => $intervention, ]); @@ -53,6 +71,11 @@ final class InterventionController extends AbstractController #[Route('/{id}/edit', name: 'app_intervention_edit', methods: ['GET', 'POST'])] public function edit(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response { + // Vérification de sécurité : un chauffagiste ne peut modifier que ses propres interventions + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $intervention->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas modifier cette intervention.'); + } + $form = $this->createForm(InterventionType::class, $intervention); $form->handleRequest($request); @@ -71,7 +94,12 @@ final class InterventionController extends AbstractController #[Route('/{id}', name: 'app_intervention_delete', methods: ['POST'])] public function delete(Request $request, Intervention $intervention, EntityManagerInterface $entityManager): Response { - if ($this->isCsrfTokenValid('delete'.$intervention->getId(), $request->getPayload()->getString('_token'))) { + // Vérification de sécurité : un chauffagiste ne peut supprimer que ses propres interventions + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $intervention->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer cette intervention.'); + } + + if ($this->isCsrfTokenValid('delete'.$intervention->getId(), $request->get('csrf_token'))) { $entityManager->remove($intervention); $entityManager->flush(); } diff --git a/src/Controller/SkillController.php b/src/Controller/SkillController.php index 1cc04d1..e8d4e1f 100644 --- a/src/Controller/SkillController.php +++ b/src/Controller/SkillController.php @@ -17,6 +17,7 @@ final class SkillController extends AbstractController #[Route(name: 'app_skill_index', methods: ['GET'])] public function index(SkillRepository $skillRepository): Response { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); return $this->render('skill/admin.html.twig', [ 'skills' => $skillRepository->findAll(), ]); @@ -25,6 +26,7 @@ final class SkillController extends AbstractController #[Route('/new', name: 'app_skill_new', methods: ['GET', 'POST'])] public function new(Request $request, EntityManagerInterface $entityManager): Response { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); $skill = new Skill(); $form = $this->createForm(SkillType::class, $skill); $form->handleRequest($request); @@ -53,6 +55,7 @@ final class SkillController extends AbstractController #[Route('/{id}/edit', name: 'app_skill_edit', methods: ['GET', 'POST'])] public function edit(Request $request, Skill $skill, EntityManagerInterface $entityManager): Response { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); $form = $this->createForm(SkillType::class, $skill); $form->handleRequest($request); @@ -71,7 +74,8 @@ final class SkillController extends AbstractController #[Route('/{id}', name: 'app_skill_delete', methods: ['POST'])] public function delete(Request $request, Skill $skill, EntityManagerInterface $entityManager): Response { - if ($this->isCsrfTokenValid('delete'.$skill->getId(), $request->getPayload()->getString('_token'))) { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); + if ($this->isCsrfTokenValid('delete'.$skill->getId(), $request->get('csrf_token'))) { $entityManager->remove($skill); $entityManager->flush(); } diff --git a/src/Controller/StockController.php b/src/Controller/StockController.php index 0252e71..1168128 100644 --- a/src/Controller/StockController.php +++ b/src/Controller/StockController.php @@ -53,6 +53,11 @@ final class StockController extends AbstractController #[Route('/{id}/edit', name: 'app_stock_edit', methods: ['GET', 'POST'])] public function edit(Request $request, Stock $stock, EntityManagerInterface $entityManager): Response { + // Si un chauffagiste essaie de modifier un stock d'admin + if ($this->isGranted('ROLE_CHAUFFAGISTE')) { + throw $this->createAccessDeniedException('Vous ne pouvez pas modifier ce stock.'); + } + $form = $this->createForm(StockType::class, $stock); $form->handleRequest($request); @@ -71,7 +76,7 @@ final class StockController extends AbstractController #[Route('/{id}', name: 'app_stock_delete', methods: ['POST'])] public function delete(Request $request, Stock $stock, EntityManagerInterface $entityManager): Response { - if ($this->isCsrfTokenValid('delete'.$stock->getId(), $request->getPayload()->getString('_token'))) { + if ($this->isCsrfTokenValid('delete'.$stock->getId(), $request->get('csrf_token'))) { $entityManager->remove($stock); $entityManager->flush(); } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 7b8d7c0..3cf65ff 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -9,13 +9,15 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; -use Symfony\Component\Routing\Attribute\Route; -#[Route('/user')] -final class UserController extends AbstractController +/** + * @Route("/user") + */ +class UserController extends AbstractController { - #[Route(name: 'app_user_index', methods: ['GET'])] + /** + * @Route("/", name="app_user_index", methods={"GET"}) + */ public function index(UserRepository $userRepository): Response { return $this->render('user/index.html.twig', [ @@ -23,19 +25,16 @@ final class UserController extends AbstractController ]); } - #[Route('/new', name: 'app_user_new', methods: ['GET', 'POST'])] - public function new(Request $request, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher): Response + /** + * @Route("/new", name="app_user_new", methods={"GET", "POST"}) + */ + public function new(Request $request, EntityManagerInterface $entityManager): Response { $user = new Utilisateur(); $form = $this->createForm(UserType::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - // Hash du mot de passe - $plainPassword = $form->get('plainPassword')->getData(); - $hashedPassword = $passwordHasher->hashPassword($user, $plainPassword); - $user->setPassword($hashedPassword); - $entityManager->persist($user); $entityManager->flush(); @@ -48,7 +47,9 @@ final class UserController extends AbstractController ]); } - #[Route('/{id}', name: 'app_user_show', methods: ['GET'])] + /** + * @Route("/{id}", name="app_user_show", methods={"GET"}) + */ public function show(Utilisateur $user): Response { return $this->render('user/show.html.twig', [ @@ -56,9 +57,24 @@ final class UserController extends AbstractController ]); } - #[Route('/{id}/edit', name: 'app_user_edit', methods: ['GET', 'POST'])] + /** + * @Route("/{id}/edit", name="app_user_edit", methods={"GET", "POST"}) + */ public function edit(Request $request, Utilisateur $user, EntityManagerInterface $entityManager): Response { + // Si l'utilisateur est un secrétaire et qu'il essaie de modifier un autre secrétaire + if ($this->isGranted('ROLE_SECRETAIRE') && $user->hasRole('ROLE_SECRETAIRE')) { + throw $this->createAccessDeniedException('Vous ne pouvez pas modifier un autre secrétaire.'); + } + + // Si l'utilisateur est un chauffagiste et qu'il essaie de modifier un admin + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $user->hasRole('ROLE_ADMIN')) { + throw $this->createAccessDeniedException('Vous ne pouvez pas modifier un admin.'); + } + + // On s'assure que seul un admin peut éditer un autre admin + $this->denyAccessUnlessGranted('ROLE_ADMIN'); + $form = $this->createForm(UserType::class, $user); $form->handleRequest($request); @@ -74,10 +90,25 @@ final class UserController extends AbstractController ]); } - #[Route('/{id}', name: 'app_user_delete', methods: ['POST'])] + /** + * @Route("/{id}", name="app_user_delete", methods={"POST"}) + */ public function delete(Request $request, Utilisateur $user, EntityManagerInterface $entityManager): Response { - if ($this->isCsrfTokenValid('delete'.$user->getId(), $request->getPayload()->getString('_token'))) { + // Si l'utilisateur est un secrétaire et qu'il essaie de supprimer un autre secrétaire + if ($this->isGranted('ROLE_SECRETAIRE') && $user->hasRole('ROLE_SECRETAIRE')) { + throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer un autre secrétaire.'); + } + + // Si l'utilisateur est un chauffagiste et qu'il essaie de supprimer un admin + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $user->hasRole('ROLE_ADMIN')) { + throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer un admin.'); + } + + // On s'assure que seul un admin peut supprimer un autre admin + $this->denyAccessUnlessGranted('ROLE_ADMIN'); + + if ($this->isCsrfTokenValid('delete' . $user->getId(), $request->get('csrf_token'))) { $entityManager->remove($user); $entityManager->flush(); } diff --git a/src/Controller/VehicleController.php b/src/Controller/VehicleController.php index 4921a28..6267c48 100644 --- a/src/Controller/VehicleController.php +++ b/src/Controller/VehicleController.php @@ -17,8 +17,13 @@ final class VehicleController extends AbstractController #[Route(name: 'app_vehicle_index', methods: ['GET'])] public function index(VehicleRepository $vehicleRepository): Response { + // Admin peut voir tous les véhicules, chauffagiste ne peut voir que ses véhicules + $vehicles = $this->isGranted('ROLE_CHAUFFAGISTE') + ? $vehicleRepository->findByUser($this->getUser()) // Filtre les véhicules par utilisateur + : $vehicleRepository->findAll(); // Les admins voient tout + return $this->render('vehicle/index.html.twig', [ - 'vehicles' => $vehicleRepository->findAll(), + 'vehicles' => $vehicles, ]); } @@ -30,6 +35,11 @@ final class VehicleController extends AbstractController $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + // Si l'utilisateur est un chauffagiste, on associe le véhicule à lui + if ($this->isGranted('ROLE_CHAUFFAGISTE')) { + $vehicle->setUser($this->getUser()); + } + $entityManager->persist($vehicle); $entityManager->flush(); @@ -45,6 +55,11 @@ final class VehicleController extends AbstractController #[Route('/{id}', name: 'app_vehicle_show', methods: ['GET'])] public function show(Vehicle $vehicle): Response { + // Si l'utilisateur est un chauffagiste et essaie de voir un véhicule d'un autre chauffagiste, on bloque + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $vehicle->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas voir ce véhicule.'); + } + return $this->render('vehicle/show.html.twig', [ 'vehicle' => $vehicle, ]); @@ -53,6 +68,11 @@ final class VehicleController extends AbstractController #[Route('/{id}/edit', name: 'app_vehicle_edit', methods: ['GET', 'POST'])] public function edit(Request $request, Vehicle $vehicle, EntityManagerInterface $entityManager): Response { + // Vérifier si un chauffagiste essaie de modifier un véhicule d'un autre chauffagiste + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $vehicle->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas modifier ce véhicule.'); + } + $form = $this->createForm(VehicleType::class, $vehicle); $form->handleRequest($request); @@ -71,7 +91,12 @@ final class VehicleController extends AbstractController #[Route('/{id}', name: 'app_vehicle_delete', methods: ['POST'])] public function delete(Request $request, Vehicle $vehicle, EntityManagerInterface $entityManager): Response { - if ($this->isCsrfTokenValid('delete'.$vehicle->getId(), $request->getPayload()->getString('_token'))) { + // Vérification de sécurité : un chauffagiste ne peut supprimer un véhicule d'un autre chauffagiste + if ($this->isGranted('ROLE_CHAUFFAGISTE') && $vehicle->getUser() !== $this->getUser()) { + throw $this->createAccessDeniedException('Vous ne pouvez pas supprimer ce véhicule.'); + } + + if ($this->isCsrfTokenValid('delete'.$vehicle->getId(), $request->get('csrf_token'))) { $entityManager->remove($vehicle); $entityManager->flush(); }