mardi 23 mai 2017

Connexion d'un User dans Symfony via un Listener

On peut avoir besoin de connecter un utilisateur via du code, par exemple pour des tests d'url via steevanb/php-url-test.
La documentation de Symfony est une base pour comprendre le mécanisme, il manque cependant quelques détails.

Vous trouverez ci-dessous un exemple complet, qui créé LoginListener, pour connecter votre utilisateur avant que le firewall n'essaye de le récupérer.
Notez que pour des raisons de sécurité, je met ce code dans app/AppTestKernel.php. Ce fichier sera supprimé de l'environnement de prod.

Création du listener dans AppTestKernel.php :
protected function getContainerBuilder(): ContainerBuilder { $container = parent::getContainerBuilder(); $container->setDefinition( 'foo.login_event_listener', new Definition( LoginEventListener::class, [ new Reference('security.token_storage'), new Reference('foo.user_provider'), new Reference('event_dispatcher'), new Reference('session'), new Reference('request_stack') ] ) ); return $container; }
Ajout du listener sur kernel.request, dans AppTestKernel.php :
public function boot(): void { parent::boot(); $this ->getContainer() ->get('event_dispatcher') // priorité 9 parceque le firewall qui redirige sur la page de login a une priorité de 8 ->addListenerService('kernel.request', ['foo.login_event_listener', 'login'], 9); }
Listener qui log le user admin :
<?php declare(strict_types=1); namespace Foo; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpFoundation\{ RequestStack, Session\SessionInterface }; use Symfony\Component\Security\{ Core\Authentication\Token\Storage\TokenStorageInterface, Core\Authentication\Token\UsernamePasswordToken, Core\User\UserProviderInterface, Http\Event\InteractiveLoginEvent }; class LoginEventListener { /** @var TokenStorageInterface */ protected $tokenStorage; /** @var UserProviderInterface */ protected $userProvider; /** @var EventDispatcher */ protected $eventDispatcher; /** @var SessionInterface */ protected $session; /** @var RequestStack */ protected $requestStack; public function __construct( TokenStorageInterface $tokenStorage, UserProviderInterface $userProvider, EventDispatcher $eventDispatcher, SessionInterface $session, RequestStack $requestStack ) { $this->tokenStorage = $tokenStorage; $this->userProvider = $userProvider; $this->eventDispatcher = $eventDispatcher; $this->session = $session; $this->requestStack = $requestStack; } public function login(GetResponseEvent $event): void { // récupération de l'utilisateur et connexion $user = $this->userProvider->loadUserByUsername('admin'); $token = new UsernamePasswordToken( $user, $user->getPassword(), 'main', $user->getRoles() ); $this->tokenStorage->setToken($token); // l'événement security.interactive_login n'est pas lancé automatiquement $event = new InteractiveLoginEvent($event->getRequest(), $token); $this->eventDispatcher->dispatch('security.interactive_login', $event); // http://symfony.com/doc/current/testing/http_authentication.html // sauvegarde du token dans la session, pour que le firewall le retrouve $this->session->set('_security_main', serialize($token)); $this->session->save(); // création du cookie // si on ne le fait pas, le firewall redirige sur la page de connexion $this->requestStack->getCurrentRequest()->cookies->set( $this->session->getName(), $this->session->getId() ); } }
Documentation Symfony

vendredi 5 mai 2017

Feature request : [symfony/console] pouvoir supprimer une commande

Le composant symfony/console permet d'exécuter des commandes PHP facilement.
Intégré dans Symfony, il permet à des bundles externes d'ajouter leurs commandes, par exemple doctrine:schema:update.
Cependant, certaines commandes ne devraient pas être accessibles en fonction de l'environnement, ou du projet.

Pour reprendre doctrine:schema:update, elle est très utile en dev, mais ne devrait pas pouvoir être lancée en prod.
Autre exemple : l'application sur laquelle je travaille est découpée en plusieurs projets, dont un qui contient toutes les fixtures, migrations, création de triggers, vues, etc.
Toute la gestion de la base de données est centralisée dans ce projet.
Donc, les commandes doctrine:* n'ont pas lieu d'être dans les autres projets, et surtout, ne profitent pas d'une éventuelle surcharge effectuée par le projet database.

Feature request #22645