As we talked about this before, you need to implement EventListener
which will listen to your onKernelRequest
.
In plain English, this means that all of your Controller Actions will execute onKernelRequest
first before giving access to the original controller. So this way you won't have to write
/**
* @Security("has_role('ROLE_ADMIN')")
*/
in every controller action.
Now, its upto you what you want to do in this method. My approach was to make a table which associates a ROLE
with a ROUTE
. This table will be comparatively big because you have to include all ROLES
you want to give access to all ROUTES
.
The table structure can be something like this:
ACCESSID ROLENAME ROUTENAME
1 ROLE_NEWUSER contacts_lookup_homepage
2 ROLE_SUPER_USER contacts_lookup_homepage
According to this table only ROLE_NEWUSER
and ROLE_SUPER_USER
are eligible to access the route contacts_lookup_homepage
This way now only those roles are allowed to access contacts_lookup_homepage
route. Now on the onKernelRequest
all you will do is query this table and check if there is a match with that role with that route. You have access to both in this method. These routes are the same as the one you define in your routing.yml
file of every route. If you're not sure, it looks something like this:
contacts_lookup_homepage:
path: /Contacts/Lookup
defaults: { _controller: ContactsLookupBundle:Default:index }
Now finally in your onKernelRequest
you can do something like this:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$route = $request->attributes->get('_route');
$routeArr = array('fos_js_routing_js', 'fos_user_security_login', '_wdt'); //These are excluded routes. These are always allowed. Required for login page
$roleArr = $this->token_storage->getToken()->getUser()->getRoles();
if(!is_int(array_search($route, $routeArr))) //This is for excluding routes that you don't want to check for.
{
//Check for a matching role and route
$qb = $this->em->getRepository('AppBundle:UserAccess')->createQueryBuilder('o');
$qb
->select('o')
->where('o.ROLENAME IN (:roleArr)')
->setParameter('roleArr', $roleArr)
->andWhere('o.ROUTENAME = :route')
->setParameter('route', $route)
;
$result = $qb->getQuery()->getArrayResult();
if(empty($result))
{
//A matching role and route was not found so we do not give access to the user here and redirect to another page.
$event->setResponse(new RedirectResponse($this->router->generate('user_management_unauthorized_user', array())));
}
}
}
The services.yml
can be like this:
services:
app.tokens.action_listener:
class: EventListenerBundleEventListenerTokenListener
arguments:
entityManager: "@doctrine.orm.entity_manager"
token_storage: "@security.token_storage"
templating: "@templating"
router: "@router"
resolver: "@controller_resolver"
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
This will guarantee that no unauthorized user accesses the controller action which is not authorized. I hope that gives you an idea about the implementation.