Estoy intentando generar un log automático para lo que registro algunos datos dependiendo de las acciones realizadas en la aplicación, necesito registrar los inicios de sesión para lo que hice un event-listener:
<?php namespace AppBundle\EventListener; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Security\Core\Security; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use AppBundle\Entity\Log; class SecurityListener extends Controller{ public function __construct(Security $security, Session $session) { $this->security = $security; $this->session = $session; } public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) { $log = new Log(); $em = $this->getDoctrine()->getManager(); $log->setCategoria('SESION'); $log->setTipo('LOGIN'); $log->setFecha(strftime('%Y-%m-%d')); $log->setHora(strftime('%H:%M')); $log->setAccion('Se ha iniciado sesión'); $em->persist($log); $em->flush(); } }
y este es mi config.yml
parameters: locale: en account.security_listener.class: AppBundle\EventListener\SecurityListener services: account.security_listener: class: %account.security_listener.class% arguments: ['@security.token_storage', '@session'] tags: - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }
AL momento de logear me arroja el siguiente error:
Catchable Fatal Error: Argument 1 passed to AppBundle\EventListener\SecurityListener::__construct() must be an instance of Symfony\Component\Security\Core\Security, instance of Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage given, called in C:\Users\Cristopher\Desktop\AppInventario\app_inventario\app\cache\dev\appDevDebugProjectContainer.php on line 302 and defined
Respuestas
Hola @MauriWilde,
El error se produce porque en el servicio estás inyectando el @security.token_storage pero en tu constructor definiste como primer parámetro Security $security.
Otra cosa que veo es, no se porque extiendes de la clase controller si es para hacer persistencia deberías inyectar el Object_manager o el entity_manager directamente desde el servicio.
Y por último, este es uno de esos casos en que yo inyectaría el Service_container completo, me disculparan si no es la mejor práctica, pero.....
public function __construct(ContainerInterface $container, Session $session) { $this->container = $container; $this->session = $session; } public function onInteractiveLogin(InteractiveLoginEvent $event) { $user = $event->getAuthenticationToken()->getUser(); ... }
El $user sería para obtener el usuario logueado, ya teniendo eso puedes hacer la persistencia a la base de datos.
Espero que sea de ayuda,
Saludos,
@miguelplazasr
Muchas gracias por tu ayuda @miguelplazasr
gracias a ella me di cuenta de los errores que estaba cometiendo, y pude reparar el funcionamiento, te comento como lo resolví:
Modifiqué los servicios.
services: account.security_listener: class: %account.security_listener.class% arguments: ['@security.context', '@doctrine'] tags: - { name: 'kernel.event_listener', event: 'security.interactive_login' }
Y finalmente creé esta clase, la aplicación solo utilizara una sesión de administrador por lo que no necesito rescatar el usuario.
<?php namespace AppBundle\EventListener; use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine; use AppBundle\Entity\Log; class SecurityListener{ private $securityContext; private $em; public function __construct(SecurityContext $securityContext, Doctrine $doctrine) { $this->securityContext = $securityContext; $this->em = $doctrine->getEntityManager(); } public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) { if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')) { $log = new Log(); $log->setCategoria('SESION'); $log->setTipo('LOGIN'); $log->setFecha(strftime('%Y-%m-%d')); $log->setHora(strftime('%H:%M')); $log->setAccion('Se ha iniciado sesión'); $this->em->persist($log); $this->em->flush(); } } }
Espero que la información le sirva a alguien más que necesite realizar una acción similar, saludos.
@MauriWilde
Bueno ya veo que lo resolviste, pero ya te estaba respondiendo dejo mi solución en un proyecto hecho por mi.
<?php namespace UserSecurityBundle\Listener; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\DependencyInjection\ContainerInterface; use UserSecurityBundle\Entity\Log; /** * Custom login listener. */ class LoginListener { private $container; /** * Constructor * * @param ContainerInterface $container */ public function __construct(ContainerInterface $container) { $this->container = $container; } /** * Do the magic. * * @param Event $event */ public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) { $em = $this->container->get('doctrine')->getManager(); $entity = $event->getAuthenticationToken()->getUser(); $entity->setLastConexion(time()); $em->persist($entity); $name = $entity->getUsername(); $log = new Log(); $log->setIp($this->container->get('request')->getClientIp()); $log->setShowText("Se inició la sesión del usuario {$name}."); $log->setOperation('Inicio de sesión'); $log->setUsername($name); $log->setEntityId($entity->getId()); $em->persist($log); $em->flush(); } }
y finalmente el servicio que llama a esto por supuesto:
services: login_listener: class: UserSecurityBundle\Listener\LoginListener arguments: ['@service_container'] tags: - { name: kernel.event_listener, event: security.interactive_login }
Adáptalo a tu situación pero la idea de un listener es esa.
Suerte, espero haber ayudado.
@jdvellon