<?php /****************************************************************************** * Copyright (c) Echo-numeric 2020-2023. * ******************************************************************************/ namespace App\Listener; use App\Annotation\Acl as AclAnnotation; use App\Entity\User; use App\Services\Common\AclService; use Doctrine\Common\Annotations\Reader; use Psr\Cache\InvalidArgumentException; use ReflectionClass; use ReflectionException; use RuntimeException; use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Security; class AclAnnotationListener { private Reader $annotationReader; private Security $security; private AclService $aclService; private ControllerEvent $event; public function __construct( Reader $annotationReader, Security $security, AclService $aclService ) { $this->annotationReader = $annotationReader; $this->security = $security; $this->aclService = $aclService; } /** * @param ControllerEvent $event * * @return void * @throws ReflectionException|InvalidArgumentException */ public function onKernelController( ControllerEvent $event ): void { $this->event = $event; if ( !$event->isMainRequest() ) { return; } $controllers = $event->getController(); if ( !is_array( $controllers ) ) { return; } $this->handleAnnotation( $controllers ); } /** * @param iterable $controllers * * @return void * * @throws ReflectionException * @throws InvalidArgumentException */ private function handleAnnotation( iterable $controllers ): void { [ $controller, $method ] = $controllers; try { $controller = new ReflectionClass( $controller ); } catch ( ReflectionException $e ) { throw new RuntimeException( 'Failed to read annotation!' ); } $this->handleMethodAnnotation( $controller, $method ); } /** * @param ReflectionClass $controller * @param string $method * * @return void * * @throws ReflectionException * @throws InvalidArgumentException */ private function handleMethodAnnotation( ReflectionClass $controller, string $method ): void { $method = $controller->getMethod( $method ); $annotation = $this->annotationReader->getMethodAnnotation( $method, AclAnnotation::class ); if ( $annotation instanceof AclAnnotation ) { /** @var User $user */ $user = $this->security->getUser(); foreach ( $annotation->rules as $slug => $action ) { $request = $this->event->getRequest(); switch ( $request->get( '_route' ) ) { case 'front_catalogue_homepage': $slug .= '.' . $request->get( 'catalogue' ); break; case 'front_catalogue_product': case 'front_recompense_boutique_collective_show': $slug .= '.' . $request->get( 'catalogueType' ); break; default: break; } if ( $this->aclService->userIsGranted( $user, $slug, $action, $this->event->getRequest() ->get( '_env' ) ?? 'front' ) === TRUE ) { return; } } throw new AccessDeniedException( 'Vous ne disposez pas des droits suffisants pour cette page. ("' . json_encode( $annotation->rules ) . ')' ); } } }