<?php
namespace App\EventSubscriber;
use App\Constants\ACL;
use App\Entity\User;
use App\Services\Common\AclServiceV2;
use JsonException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
/**
* Classe AclAccessSubscriber
*
* Cette classe est un abonné aux événements qui gère les droits d'accès aux différentes parties de l'application.
* Elle écoute les requêtes entrantes et applique les règles d'accès définies.
*
* Méthodes :
* - __construct: Constructeur de la classe, initialise les dépendances nécessaires pour la gestion des accès.
* - onKernelRequest: Méthode appelée lors de l'événement de requête du noyau. Utilisée pour vérifier les droits
* d'accès de l'utilisateur courant pour la requête en cours.
* - isUriPublic: (Méthode privée) Détermine si l'URI courante est accessible publiquement sans restrictions
* d'accès.
*
* @package App\EventSubscriber
*/
class AclAccessSubscriber implements EventSubscriberInterface
{
private AclServiceV2 $aclService;
private Security $security;
/**
* @param AclServiceV2 $aclService
* @param Security $security
*/
public function __construct( AclServiceV2 $aclService, Security $security )
{
$this->aclService = $aclService;
$this->security = $security;
}
/**
* Méthode appelée lors de l'événement de requête du noyau. Utilisée pour vérifier les droits d'accès de
* l'utilisateur courant pour la requête en cours.
*
* @return string[]
*/
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
}
/**
* Listener sur la possibilité d'accéder ou non à la route de la request
*
* Vérifie si la route est "publique"
* Vérifie si le currentUser a le droit d'accéder à la route → 403 si userIsGranted retourne FALSE
* Si la route concerne le back et que le currentUser est un ROLE_USER retourne une 403
*
* @param RequestEvent $event
*
* @return void
*
* @throws JsonException
*/
public function onKernelRequest( RequestEvent $event ): void
{
$request = $event->getRequest();
/** @var User $currentUser */
$currentUser = $this->security->getUser();
$env = $request->get( '_env' ) ?? ACL::FRONT_ENV;
$route = $request->get( '_route' );
$params = json_encode( $request->get( '_route_params' ), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE );
if (
$route === NULL || // pas de route
$this->isUriPublic( $request->getRequestUri() )
) {
return;
}
$isGranted = $this->aclService->userIsGranted(
$currentUser,
[
'route' => $route,
'params' => $params,
'component' => ACL::ACL_NO_COMPONENT,
'slug' => ACL::ACL_NO_SLUG,
'action' => ACL::READ,
'env' => $env,
]
);
// on bloque en plus l'accès back pour les user, en cas de soucis sur le security.yaml
if ( !$isGranted || ( $env === ACL::BACK_ENV && $currentUser->isUser() ) ) {
throw new AccessDeniedException( 'Vous ne disposez pas des droits suffisants pour cette page' );
}
}
/**
* Vérifie que l'url ne fait pas partie de la liste des urls accessibles par tout le monde
*
* @param string $uriRequested
*
* @return bool
*/
private function isUriPublic( string $uriRequested ): bool
{
$uriPublic = [
'/build/',
'/js/',
'/_wdt/',
'/_profiler/',
// uri venant de security.yaml access_control
'/static-file',
'/static-project-file',
'/cron',
'/cache-clear',
'/closed-platform',
'/wm-api/login',
'/wm-api/doc',
'/white-mark',
'/api',
'/portal',
'/monetico',
'/stripe',
'/dtv/api',
'/ems-api',
'/charitips/webhook/donation-success',
'/saml/login',
'/saml/metadata',
'/saml/acs',
'/saml/logout',
'/login',
'/register',
'/membership/register',
'/reset-password',
'/profil/accept-cgu-first',
'/profil/welcome-register',
'/bo-parameters',
'/document',
'/front-css-custom.css',
'/dev',
'/version',
'/bdd-up',
'/upgrade',
'/catalogue/search.json',
'/create-developer',
];
$selected = array_filter( $uriPublic, static function ( $el ) use ( $uriRequested ) {
return strpos( $uriRequested, $el ) === 0;
} );
return $selected !== [];
}
}