K
K
Kripto772020-08-23 16:51:20
symfony
Kripto77, 2020-08-23 16:51:20

How to validate user on every authentication in Symfony 5?

There is an administrator and ordinary users. An ordinary user can be blocked at any moment - then at the first authentication it is necessary to check whether he is blocked - if so - log out and send to the authorization form.
Tried via EventListener - but nothing happens - AuthListener is not called.
There is no reaction to event: security.interactive_login either

#config/services.xml
services:
    App\EventListener\AuthListener:
        tags:
            - { name: kernel.event_listener, event: security.authentication.success, method: onSecurityAuthenticationSuccess }


//App/EventListener/AuthListener.php
namespace App\EventListener;

use App\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Security;

class AuthListener
{
    /** @var User $user */
    private $user;

    public function __construct(Security $security)
    {
        $this->user = $security->getUser();
    }

    public function onSecurityAuthenticationSuccess()
    {
        if ($this->user->isBlocked()) {
            file_put_contents('onSecurityAuthenticationSuccess.txt', $this->user->getId() . ' - User blocked', FILE);
            return new RedirectResponse('/logout');
        }
    }
}


Thanks in advance

Answer the question

In order to leave comments, you need to log in

2 answer(s)
K
Kripto77, 2020-08-24
@Kripto77

Thanks to everyone for the comments - they brought me to the right ideas.
As a result, I came to a not very beautiful solution through the EventSubscriber and the global onKernelRequest (it works on any request). I would be grateful if someone could suggest a better solution.

//src/Events/GlobalSubscriber.php

namespace App\Events;

use App\Entity\User;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class GlobalSubscriber implements EventSubscriberInterface
{
    /** @var RouterInterface $router */
    private $router;

    /** @var TokenStorageInterface $tokenStorage */
    private $tokenStorage;

    /**
     * @param RouterInterface $router
     * @param TokenStorageInterface $tokenStorage
     */
    public function __construct(RouterInterface $router, TokenStorageInterface $tokenStorage)
    {
        $this->router = $router;
        $this->tokenStorage = $tokenStorage;
    }

    public function onKernelRequest(RequestEvent $event)
    {
        if(!$event->isMasterRequest())
            return;

        $this->isUserBlocked($event);
    }

    /**
     * Проверка блокировки пользователя
     * @param RequestEvent $event
     */
    private function isUserBlocked(RequestEvent $event)
    {
        if (empty($this->tokenStorage->getToken()) || !$this->tokenStorage->getToken()->getUser() instanceof User) return;

        /** @var User $user */
        $user = $this->tokenStorage->getToken()->getUser();

        if ($user->isBlocked()) {
            $event->setResponse(new RedirectResponse($this->router->generate('app_logout')));
        }

    }

    public static function getSubscribedEvents()
    {
        return [
            // Правка от @Flying
            KernelEvents::REQUEST => 'onKernelRequest'
            // Старый вариант - RequestEvent::class => 'onKernelRequest'
        ];
    }
}

It's strange why the EventListener didn't work - I did it according to off-docs https://symfony.com/doc/current/event_dispatcher.h...

D
Dmitry, 2020-08-23
@gebrak

Perhaps the simplest solution would be to remove the roles from the user when blocking (User->setRoles([])). Then the blocked user will have different data in the session and in the database on the next request, and he will be redirected to the account login form. There, through UserChecker, you can show him a message that the account is blocked.
https://symfony.com/doc/current/security/user_prov...
Of course, this requires that the user roles be stored in the session (symfony does this by default), so if your User class implements \Serializable, then you need to serialize/unserialize or add roles, or the blocking flag itself.
Another option is to check the status in the UserProvider in the refreshUser() method and throw a UsernameNotFoundException.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question