From b982a0539a37de9d7e5b09734fbdbe0b5491718f Mon Sep 17 00:00:00 2001 From: numew Date: Fri, 14 Jun 2024 12:28:31 +0200 Subject: [PATCH] revamp fileVoter #2649 --- src/Controller/Back/SignalementController.php | 2 +- .../Back/SignalementFileController.php | 9 +-- .../Back/SignalementVisitesController.php | 25 +++--- .../Security/SecurityController.php | 2 +- src/Security/Voter/FileVoter.php | 77 +++++-------------- src/Security/Voter/InterventionVoter.php | 59 -------------- src/Security/Voter/SignalementVoter.php | 6 +- templates/back/signalement/view.html.twig | 2 +- .../view/photos-documents.html.twig | 2 +- .../visites/modals/visites-modals.html.twig | 2 +- .../view/visites/visites-buttons.html.twig | 8 +- .../view/visites/visites-list.html.twig | 2 +- 12 files changed, 44 insertions(+), 152 deletions(-) delete mode 100755 src/Security/Voter/InterventionVoter.php diff --git a/src/Controller/Back/SignalementController.php b/src/Controller/Back/SignalementController.php index 2c45981a9..e2239b3f2 100755 --- a/src/Controller/Back/SignalementController.php +++ b/src/Controller/Back/SignalementController.php @@ -53,12 +53,12 @@ public function viewSignalement( ): Response { /** @var User $user */ $user = $this->getUser(); - $this->denyAccessUnlessGranted('SIGN_VIEW', $signalement); if (Signalement::STATUS_ARCHIVED === $signalement->getStatut()) { $this->addFlash('error', "Ce signalement a été archivé et n'est pas consultable."); return $this->redirectToRoute('back_index'); } + $this->denyAccessUnlessGranted('SIGN_VIEW', $signalement); $eventDispatcher->dispatch( new SignalementViewedEvent($signalement, $user), diff --git a/src/Controller/Back/SignalementFileController.php b/src/Controller/Back/SignalementFileController.php index 87959ddb5..c575801c0 100755 --- a/src/Controller/Back/SignalementFileController.php +++ b/src/Controller/Back/SignalementFileController.php @@ -32,11 +32,6 @@ public function generatePdfSignalement( MessageBusInterface $messageBus ): Response { $this->denyAccessUnlessGranted('SIGN_VIEW', $signalement); - if (Signalement::STATUS_ARCHIVED === $signalement->getStatut()) { - $this->addFlash('error', "Ce signalement a été archivé et n'est pas consultable."); - - return $this->redirectToRoute('back_index'); - } /** @var User $user */ $user = $this->getUser(); @@ -64,7 +59,7 @@ public function addFileSignalement( EntityManagerInterface $entityManager, SignalementFileProcessor $signalementFileProcessor ): Response { - $this->denyAccessUnlessGranted('FILE_CREATE', $signalement); + $this->denyAccessUnlessGranted('SIGN_EDIT', $signalement); if (!$this->isCsrfTokenValid('signalement_add_file_'.$signalement->getId(), $request->get('_token')) || !$files = $request->files->get('signalement-add-file')) { if ($request->isXmlHttpRequest()) { return $this->json(['response' => 'Token CSRF invalide ou paramètre manquant, veuillez rechargez la page'], Response::HTTP_BAD_REQUEST); @@ -113,7 +108,7 @@ public function fileWaitingSuiviSignalement( EntityManagerInterface $entityManager, SuiviManager $suiviManager, ): JsonResponse { - $this->denyAccessUnlessGranted('FILE_CREATE', $signalement); + $this->denyAccessUnlessGranted('SIGN_EDIT', $signalement); $fileRepository = $entityManager->getRepository(File::class); $files = $fileRepository->findBy(['signalement' => $signalement, 'isWaitingSuivi' => true]); if (!\count($files)) { diff --git a/src/Controller/Back/SignalementVisitesController.php b/src/Controller/Back/SignalementVisitesController.php index 2d77136f6..e13074317 100755 --- a/src/Controller/Back/SignalementVisitesController.php +++ b/src/Controller/Back/SignalementVisitesController.php @@ -31,13 +31,6 @@ class SignalementVisitesController extends AbstractController private function getSecurityRedirect(Signalement $signalement, Request $request, string $tokenName): ?Response { - $this->denyAccessUnlessGranted('SIGN_VIEW', $signalement); - if (Signalement::STATUS_ARCHIVED === $signalement->getStatut()) { - $this->addFlash('error', "Ce signalement a été archivé et n'est pas consultable."); - - return $this->redirectToRoute('back_index'); - } - if (!$this->isCsrfTokenValid($tokenName, $request->get('_token'))) { $this->addFlash('error', "Erreur de sécurisation de l'envoi de données."); @@ -136,13 +129,13 @@ public function cancelVisiteFromSignalement( ): Response { $requestData = $request->get('visite-cancel'); - $intervention = $interventionRepository->findOneBy(['id' => $requestData['intervention']]); + $intervention = $interventionRepository->findOneBy(['id' => $requestData['intervention'], 'signalement' => $signalement]); if (!$intervention) { $this->addFlash('error', "Cette visite n'existe pas."); return $this->redirectToRoute('back_index'); } - $this->denyAccessUnlessGranted('INTERVENTION_EDIT_VISITE', $intervention); + $this->denyAccessUnlessGranted('SIGN_ADD_VISITE', $signalement); $errorRedirect = $this->getSecurityRedirect( $signalement, @@ -180,13 +173,13 @@ public function rescheduleVisiteFromSignalement( ): Response { $requestRescheduleData = $request->get('visite-reschedule'); - $intervention = $interventionRepository->findOneBy(['id' => $requestRescheduleData['intervention']]); + $intervention = $interventionRepository->findOneBy(['id' => $requestRescheduleData['intervention'], 'signalement' => $signalement]); if (!$intervention) { $this->addFlash('error', "Cette visite n'existe pas."); return $this->redirectToRoute('back_index'); } - $this->denyAccessUnlessGranted('INTERVENTION_EDIT_VISITE', $intervention); + $this->denyAccessUnlessGranted('SIGN_ADD_VISITE', $signalement); $errorRedirect = $this->getSecurityRedirect( $signalement, @@ -244,13 +237,13 @@ public function confirmVisiteFromSignalement( ): Response { $requestData = $request->get('visite-confirm'); - $intervention = $interventionRepository->findOneBy(['id' => $requestData['intervention']]); + $intervention = $interventionRepository->findOneBy(['id' => $requestData['intervention'], 'signalement' => $signalement]); if (!$intervention) { $this->addFlash('error', "Cette visite n'existe pas."); return $this->redirectToRoute('back_index'); } - $this->denyAccessUnlessGranted('INTERVENTION_EDIT_VISITE', $intervention); + $this->denyAccessUnlessGranted('SIGN_ADD_VISITE', $signalement); $errorRedirect = $this->getSecurityRedirect( $signalement, @@ -294,13 +287,13 @@ public function editVisiteFromSignalement( ): Response { $requestData = $request->get('visite-edit'); - $intervention = $interventionRepository->findOneBy(['id' => $requestData['intervention']]); + $intervention = $interventionRepository->findOneBy(['id' => $requestData['intervention'], 'signalement' => $signalement]); if (!$intervention) { $this->addFlash('error', "Cette visite n'existe pas."); return $this->redirectToRoute('back_index'); } - $this->denyAccessUnlessGranted('INTERVENTION_EDIT_VISITE', $intervention); + $this->denyAccessUnlessGranted('SIGN_ADD_VISITE', $signalement); $errorRedirect = $this->getSecurityRedirect( $signalement, @@ -341,7 +334,7 @@ public function deleteRapportVisiteFromSignalement( EntityManagerInterface $entityManager, UploadHandlerService $uploadHandlerService, ): Response { - $this->denyAccessUnlessGranted('INTERVENTION_EDIT_VISITE', $intervention); + $this->denyAccessUnlessGranted('SIGN_ADD_VISITE', $intervention->getSignalement()); if (!$this->isCsrfTokenValid('delete_rapport', $request->get('_token')) || $intervention->getSignalement()->getId() !== $signalement->getId() || $intervention->getFiles()->isEmpty()) { return $this->redirectToRoute('back_signalement_view', ['uuid' => $signalement->getUuid()]); } diff --git a/src/Controller/Security/SecurityController.php b/src/Controller/Security/SecurityController.php index 2ae025c65..7efb08476 100755 --- a/src/Controller/Security/SecurityController.php +++ b/src/Controller/Security/SecurityController.php @@ -42,7 +42,7 @@ public function showUploadedFile( ): BinaryFileResponse|RedirectResponse { $request = Request::createFromGlobals(); - if (!$this->isCsrfTokenValid('suivi_signalement_ext_file_view', $request->get('t')) && !$this->isGranted('FILE_VIEW', $signalement)) { + if (!$this->isCsrfTokenValid('suivi_signalement_ext_file_view', $request->get('t')) && !$this->isGranted('SIGN_VIEW', $signalement)) { throw $this->createNotFoundException(); } try { diff --git a/src/Security/Voter/FileVoter.php b/src/Security/Voter/FileVoter.php index 242c7e332..2b74fb869 100755 --- a/src/Security/Voter/FileVoter.php +++ b/src/Security/Voter/FileVoter.php @@ -6,24 +6,17 @@ use App\Entity\File; use App\Entity\Signalement; use App\Entity\User; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class FileVoter extends Voter { public const DELETE = 'FILE_DELETE'; - public const VIEW = 'FILE_VIEW'; - public const CREATE = 'FILE_CREATE'; public const EDIT = 'FILE_EDIT'; - public function __construct(private ParameterBagInterface $parameterBag) - { - } - protected function supports(string $attribute, $subject): bool { - return \in_array($attribute, [self::DELETE, self::VIEW, self::CREATE, self::EDIT]) && ($subject instanceof Signalement || $subject instanceof File); + return \in_array($attribute, [self::DELETE, self::EDIT]) && $subject instanceof File; } protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool @@ -34,39 +27,32 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $ return false; } - if (!$subject instanceof Signalement && !$subject instanceof File) { - return false; - } - if (self::DELETE !== $attribute && - self::EDIT !== $attribute && - $this->isAdminOrRTonHisTerritory($subject, $user) - ) { - return true; - } - return match ($attribute) { self::DELETE => $this->canDelete($subject, $user), - self::VIEW => $this->canView($subject, $user), - self::CREATE => $this->canCreate($subject, $user), self::EDIT => $this->canEdit($subject, $user), default => false, }; } - private function canCreate(Signalement $signalement, User $user): bool + private function canCreate(File $file, User $user): bool { - return Signalement::STATUS_ACTIVE === $signalement->getStatut() - && $this->checkSignalementPermission($signalement, $user); - } + if (Signalement::STATUS_ACTIVE !== $file->getSignalement()->getStatut()) { + return false; + } + if ($this->isAdminOrRTonHisTerritory($file, $user)) { + return true; + } - private function canView(Signalement $subject, User $user = null): bool - { - return $this->checkSignalementPermission($subject, $user); + return $file->getSignalement()->getAffectations()->filter( + function (Affectation $affectation) use ($user) { + return $affectation->getPartner()->getId() === $user->getPartner()->getId(); + } + )->count() > 0; } private function canEdit(File $file, User $user): bool { - return $this->canCreate($file->getSignalement(), $user) + return $this->canCreate($file, $user) && ( $this->isFileUploadedByUser($file, $user) || @@ -76,7 +62,7 @@ private function canEdit(File $file, User $user): bool private function canDelete(File $file, User $user): bool { - return $this->canCreate($file->getSignalement(), $user) + return $this->canCreate($file, $user) && ( $this->isFileUploadedByUser($file, $user) || @@ -94,39 +80,12 @@ private function isFileUploadedByUser(File $file, User $user): bool return null !== $file->getUploadedBy() && $file->getUploadedBy() === $user; } - private function isAdminOrRTonHisTerritory(File|Signalement $subject, User $user): bool - { - return $user->isSuperAdmin() || - ($user->isTerritoryAdmin() && $this->isOnUserTerritory($subject, $user)); - } - - private function checkSignalementPermission(Signalement $signalement, ?User $user = null): bool + private function isAdminOrRTonHisTerritory(File $subject, User $user): bool { - if (null === $user) { - return false; - } - if ($this->isAdminOrRTonHisTerritory($signalement, $user)) { + if ($user->isSuperAdmin()) { return true; } - - return $signalement->getAffectations()->filter( - function (Affectation $affectation) use ($user) { - return $affectation->getPartner()->getId() === $user->getPartner()->getId(); - } - )->count() > 0; - } - - private function isOnUserTerritory(File|Signalement $subject, User $user): bool - { - if ( - ( - $subject instanceof Signalement && $subject->getTerritory() === $user->getTerritory() - ) - || - ( - $subject instanceof File && $subject->getSignalement()->getTerritory() === $user->getTerritory() - ) - ) { + if ($user->isTerritoryAdmin() && $subject->getSignalement()->getTerritory() === $user->getTerritory()) { return true; } diff --git a/src/Security/Voter/InterventionVoter.php b/src/Security/Voter/InterventionVoter.php deleted file mode 100755 index ae448227b..000000000 --- a/src/Security/Voter/InterventionVoter.php +++ /dev/null @@ -1,59 +0,0 @@ -getUser(); - if (!$user instanceof UserInterface) { - return false; - } - - return match ($attribute) { - self::EDIT_VISITE => $this->canEditVisite($subject, $user), - default => false, - }; - } - - public function canEditVisite(Intervention $intervention, User $user): bool - { - $signalement = $intervention->getSignalement(); - if ( - Signalement::STATUS_ACTIVE !== $signalement->getStatut() && - Signalement::STATUS_NEED_PARTNER_RESPONSE !== $signalement->getStatut() - ) { - return false; - } - - $isUserInAffectedPartnerWithQualificationVisite = $signalement->getAffectations()->filter(function (Affectation $affectation) use ($user) { - return $affectation->getPartner()->getId() === $user->getPartner()->getId() - && \in_array(Qualification::VISITES, $user->getPartner()->getCompetence()) - && Affectation::STATUS_ACCEPTED == $affectation->getStatut(); - })->count() > 0; - - return $isUserInAffectedPartnerWithQualificationVisite - || $user->isTerritoryAdmin() && $user->getTerritory() === $signalement->getTerritory() - || $user->isSuperAdmin(); - } -} diff --git a/src/Security/Voter/SignalementVoter.php b/src/Security/Voter/SignalementVoter.php index d37a62918..1c4a55b89 100755 --- a/src/Security/Voter/SignalementVoter.php +++ b/src/Security/Voter/SignalementVoter.php @@ -91,6 +91,10 @@ private function canEdit(Signalement $signalement, User $user): bool private function canView(Signalement $signalement, User $user): bool { + if (Signalement::STATUS_ARCHIVED === $signalement->getStatut()) { + return false; + } + return $signalement->getAffectations()->filter(function (Affectation $affectation) use ($user) { return $affectation->getPartner()->getId() === $user->getPartner()->getId(); })->count() > 0 || $user->isTerritoryAdmin() && $user->getTerritory() === $signalement->getTerritory(); @@ -98,7 +102,7 @@ private function canView(Signalement $signalement, User $user): bool public function canAddVisite(Signalement $signalement, User $user): bool { - if (Signalement::STATUS_ACTIVE !== $signalement->getStatut() && Signalement::STATUS_NEED_PARTNER_RESPONSE !== $signalement->getStatut()) { + if (!\in_array($signalement->getStatut(), [Signalement::STATUS_ACTIVE, Signalement::STATUS_NEED_PARTNER_RESPONSE])) { return false; } diff --git a/templates/back/signalement/view.html.twig b/templates/back/signalement/view.html.twig index 565203115..980d8ffaf 100755 --- a/templates/back/signalement/view.html.twig +++ b/templates/back/signalement/view.html.twig @@ -12,7 +12,7 @@ {% if canEditNDE %} {% include '_partials/_modal_edit_nde.html.twig' %} {% endif %} - {% if is_granted('FILE_CREATE', signalement) %} + {% if is_granted('SIGN_EDIT', signalement) %} {% include '_partials/_modal_upload_files.html.twig' %} {% endif %} {% if is_granted('ROLE_ADMIN') %} diff --git a/templates/back/signalement/view/photos-documents.html.twig b/templates/back/signalement/view/photos-documents.html.twig index b959e7fa8..5654ec779 100755 --- a/templates/back/signalement/view/photos-documents.html.twig +++ b/templates/back/signalement/view/photos-documents.html.twig @@ -5,7 +5,7 @@

{{ zonetitle }}

- {% if (is_granted('FILE_CREATE', signalement)) %} + {% if (is_granted('SIGN_EDIT', signalement)) %}