简体   繁体   中英

Symfony: How to trigger custom security voter for dynamic routes

I have following setup:

  • dynamic routes loader which genrates routes and calls proper controller based on information from database about application views

  • custom security voter which verifies if user should have access to current view

I could of course call is_granted method from within proper controller and it works. What I would like to do though is to trigger voter not from within controller (which requires to repeat myself in every controller just with different view ID) but to triger it automatically for every route generated in routes loader. This way I would not have to perform standard verification which is basically the same for every route - check if user has access to this view but focus on business logic which may require additional requirement to fulfill for user to have access to this route (and this is what would be in controller).

I was going in direction that there is some parameter in route generation method:

    $route = new Route($path, $defaults, $requirements);

    $routes->add($routeName, $route);

which would allow me to trigger is_granted for created route but documentation about parameters is scarce.

Does anyone know how to pass this kind requirement to user eg. for created route require that user is_granted('VIEW_1') ?

You can add whatever you want to the defaults array and they will get passed on. For example:

cerad_game__project__game__update__by_scorer:
  path:  /project/{_project}/game/{_game}/update-by-scorer
  defaults:
    _role:       ROLE_SCORER_ADMIN
    _model:      cerad_game__project__game__update__by_scorer__model_factory
    _form:       cerad_game__project__game__update__by_scorer__form_factory
    _controller: cerad_game__project__game__update__by_scorer__controller:action
    _template: '@CeradGame/Project/Game/Update/ByScorer/GameUpdateByScorerTwigPage.html.twig'

You would then create a kernel controller listener which will be called after the controller is resolved but before the controller action method is called.

class ModelEventListener extends ContainerAware implements EventSubscriberInterface {

public static function getSubscribedEvents()
{
    return array(
        KernelEvents::CONTROLLER => array(  
            array('onControllerRole',  self::ControllerRoleEventListenerPriority),
            array('onControllerModel', self::ControllerModelEventListenerPriority),
            array('onControllerForm',  self::ControllerFormEventListenerPriority),
        ),
        KernelEvents::VIEW => array(
            array('onView', self::ViewEventListenerPriority),
        ),
    );
}

public function onControllerRole(FilterControllerEvent $event)
{
    if (!$event->getRequest()->attributes->has('_role')) return;

    $role = $event->getRequest()->attributes->get('_role');

    $securityContext = $this->container->get('security.context');

    if (!$securityContext->isGranted($role))
    {
        // For public it redirects
        // For users we see the exception
        // die('access denied ' . $event->getRequest()->attributes->get('_route'));
        throw new AccessDeniedException(); 
    }
}

Then wire it up:

cerad_core__model_event_listener:
    class:  '%cerad_core__model_event_listener__class%'
    calls:
        - [setContainer, ['@service_container']]
    tags:
        - { name: kernel.event_subscriber }

Documented here: http://symfony.com/doc/current/cookbook/event_dispatcher/event_listener.html

Note that I injected the container here instead of just the security context. I needed the container for other methods. It is also 2.7 code. Have not gotten around to updating it to 3.0 yet.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM