3

The question is simple. I am implmenting AccessDeniedListener and I get an ExceptionEvent object. From this I can get request. I want to apply certain logic ONLY if I am inside one of my firewalls defined in security.yaml.

How can I get the Firewall name from ExceptionEvent or Request instances?

EDIT: I have found this code "works"

$firewall_context_name = $event->getRequest()->attributes->get('_firewall_context');

However Im not very happy about it. There should be a FirewallContext or FirewallConfig objects retrieveable somehow, no? Thanks

class AccessDeniedListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            // the priority must be greater than the Security HTTP
            // ExceptionListener, to make sure it's called before
            // the default exception listener
            KernelEvents::EXCEPTION => ['onKernelException', 2],
        ];
    }
    
    public function onKernelException(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();
        if (!$exception instanceof AccessDeniedException) {
            return;
        }
        
        $request = $event->getRequest();

        // HOW TO GET FIREWALL NAME HERE???

security.yaml

   firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        api:
            pattern: ^/api/
            security: false
        main:
            custom_authenticators:
                - App\Security\LoginFormAuthenticator
            logout:
                path: app_logout
            lazy: true
            provider: app_user_provider
michnovka
  • 2,880
  • 3
  • 26
  • 58
  • $request->attributes->get('_firewall_context'); should point you in the right direction. – Cerad Jun 01 '21 at 19:08
  • And is there no way to fetch this as an actual FirewallContext object? – michnovka Jun 01 '21 at 19:26
  • You can ask over on the Symfony slack channel but no there is nothing that provides the firewall context that I know of. Let us know if you do find something. – Cerad Jun 01 '21 at 19:34
  • Just an alternative idea to consider: create a custom exception class extending `AccessDeniedException` , throw it instead and in listener check if exception is an instance of this custom class. – Roman Kliuchko Jun 01 '21 at 20:11
  • @msg how do I get FirewallMap instance? – michnovka Jun 01 '21 at 20:34

1 Answers1

5

As stated in the docs you linked:

This object can be accessed through the getFirewallConfig(Request $request) method of the Symfony\Bundle\SecurityBundle\Security\FirewallMap class

This class cannot be injected directly, so you'll have to configure your dependency in services.yaml using the service alias security.firewall.map (or create a service alias if you plan to use it somewhere else).

services:
  # ...
  App\Listener\AccessDeniedListener:
    arguments:
      $firewallMap: '@security.firewall.map'

Now modify your listener to receive this parameter:

class AccessDeniedListener implements EventSubscriberInterface
{
    private $firewallMap;

    public function __construct(FirewallMapInterface $firewallMap)
    {
        $this->firewallMap = $firewallMap;
    }

    // Ommited getSubscribedEvents
    
    public function onKernelException(ExceptionEvent $event): void
    {
        $request = $event->getRequest();

        $firewallConfig = $this->firewallMap->getFirewallConfig($request);

        if (null === $firewallConfig) {
            return;
        }
        $firewallName = $firewallConfig->getName();
     }
}
michnovka
  • 2,880
  • 3
  • 26
  • 58
msg
  • 7,863
  • 3
  • 14
  • 33
  • This is awesome, thank you. I have fixed the reply to getName() from FirewallConfig directly, because in my case context is empty and I cannot get the FirewallContext object. I dont see any issue with this, if you do, let me know please – michnovka Jun 01 '21 at 21:32