I use HWIOAuthBundle for Symfony.
HWI\Bundle\OAuthBundle\Security\Core\Authentication\Provider\OAuthProvider.php has function "authenticate" where create and return OAuthToken.
public function authenticate(TokenInterface $token){
...
$token = new OAuthToken($oldToken->getRawToken(), $user->getRoles());
$token->setResourceOwnerName($resourceOwner->getName());
$token->setUser($user);
$token->setAuthenticated(true);
$token->setRefreshToken($oldToken->getRefreshToken());
$token->setCreatedAt($oldToken->getCreatedAt());
return $token;
}
I need set some attribute to this OAuthToken like :
$token->setAttribute('name' => 'value');
How can i do this ?
I use service with only one function "loadUserByOAuthUserResponse" which return user object :
class PartnerEntityUserProvider implements OAuthAwareUserProviderInterface{
...
public function loadUserByOAuthUserResponse(UserResponseInterface $response){
...
return $user;
}
...
}
Edited
I found solution with using service on security.interactive_login event :
config/services.yaml
services:
App\EventListener\InteractiveLoginListener:
tags:
- { name: kernel.event_listener, event: security.interactive_login }
src/EventListener/InteractiveLoginListener.php
<?php
namespace App\EventListener;
use App\Entity\Perimetre;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class InteractiveLoginListener
{
private $security;
private $entityManager;
public function __construct(Security $security, EntityManagerInterface $entityManager)
{
$this->security = $security;
$this->entityManager = $entityManager;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$this->security->getToken()->setAttribute('perimetre', $this->entityManager->getRepository(Perimetre::class)->findOneBy(['id' => $this->security->getToken()->getUser()->getFiliale()->getPerimetre()->getId()]));
}
}
Related
Because I have many controllers, after the user is logged in, I want to transfer some user settings to Twig. But I don't want to make it in each controller:
Eg:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$user = $this->getUser();
$settings = $user->getSettings();
// ...
}
}
Is there a possibility to make it at a higher level from the call of the DefaultController?
Solution 1
Use app.user variable in Twig which is globally accessible:
{{ app.user.username }}
More info: https://symfony.com/doc/current/templates.html#the-app-global-variable
Solution 2
Write custom Twig function:
// src/Twig/UserExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Symfony\Component\Security\Core\Security;
class UserExtension extends AbstractExtension
{
Security $security
public function __construct(Security $security)
{
// Avoid calling getUser() in the constructor: auth may not
// be complete yet. Instead, store the entire Security object.
$this->security = $security;
}
public function getFunctions()
{
return [
new TwigFunction('get_user_settings', [$this, 'getUserSettings']),
];
}
public function getUserSettings()
{
$user = $this->security->getUser();
return $user->getSettings();
}
}
Usage in Twig:
{{ get_user_settings().setting1 }}
More info: https://symfony.com/doc/current/templating/twig_extension.html
How to inject current logged user: https://symfony.com/doc/current/security.html#b-fetching-the-user-from-a-service
Ok, maybe I don't describe it clearly. This is the solution I have searched for:
<?php
namespace AppBundle\Subscriber;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class ControllerSubscriber implements EventSubscriberInterface
{
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var EntityManagerInterface
*/
private $em;
/**
* ControllerSubscriber constructor.
*
* #param TokenStorageInterface $tokenStorage
* #param EntityManagerInterface $em
*/
public function __construct(TokenStorageInterface $tokenStorage, EntityManagerInterface $em)
{
$this->tokenStorage = $tokenStorage;
$this->em = $em;
}
/**
* #param FilterControllerEvent $event
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if (!$token = $this->tokenStorage->getToken()) {
return;
}
if (!$user = $token->getUser()) {
return;
}
// if no user
if (!$user instanceof UserInterface) {
return;
} else {
// .... do something
}
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return array(
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::CONTROLLER => array(array('onKernelController', 1)),
);
}
}
services.yml
app.controller.subscriber:
class: AppBundle\Subscriber\ControllerSubscriber
arguments: ["#security.token_storage","#doctrine.orm.default_entity_manager"]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
i'm working on Symfony 4.
I have a service:
App\EventListener\EbayExceptionListener:
tags:
- { name: kernel.event_listener, event: kernel.controller }
arguments: [ "#doctrine.orm.entity_manager" , "#router" , "#session" ]
And here is my listener :
<?php
namespace App\EventListener;
use App\Entity\Ebay;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\Routing\Router;
class EbayExceptionListener
{
private $repository;
private $router;
private $session;
private $redirectName = 'home';
public function __construct(EntityManager $entityManager, Router $router, Session $session)
{
$this->repository = $entityManager->getRepository(Ebay::class);
$this->router = $router;
$this->session = $session;
}
//Check if an eBay entity exist in the database, if not redirect to the form to create the eBay entity
public function onKernelController(FilterControllerEvent $event)
{
$ebay = $this->repository->findOneBy(['name' => 'Ebay']);
$request = $event->getRequest();
$routeName = $request->get('_route');
dump($routeName);
if ($routeName != "home" AND $ebay == null) {
$this->session->getFlashBag()->add('error', 'Please provide information for Ebay form');
return new RedirectResponse($this->router->generate($this->redirectName));
}
}
}
My Controller:
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HomeController extends Controller
{
/**
* #Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig', [
'controller_name' => 'HomeController',
]);
}
}
I have a problem with the var $routeName inside my Listener, in fact the value of this variable should be the name of the current route. But when I use the dump($routeName)
I have 2 results : "_wdt" and "home"
I don't know why the $request parameters are not the same when my listener is executed and after the page has been generated.
Because I have no page call "_wdt" it supposed to be "home".
Did I do something wrong ?
Thank you for your help.
I am trying to access to doctrine within an EventListener which tests a database table, if it is invalid then the user will be redirected to a config page where he will fix the issues!
The test will be executed before every call to a controller, so I will use the Event kernel.controller:
[EDITED: SOLVED, TESTING AND REDIRECTING VERY FINE]
<?php
namespace AdminBundle\EventListener;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
class BuildReferencesEventListener
{
/**
* #var EntityManager
*/
protected $em;
/**
* #var Router
*/
protected $router;
protected $references = [
1 => 'oxygen',
2 => 'pH',
3 => 'pCO2'
];
protected $redirect_route = 'references';
protected $requestStack;
protected $session;
public function __construct(EntityManager $entityManager, Router $router, RequestStack $requestStack, Session $session)
{
$this->em = $entityManager;
$this->router = $router;
$this->requestStack = $requestStack;
$this->session = $session;
}
public function onKernelController()
{
$em = $this->em;
$savedReferences = $em->getRepository('AdminBundle:ParamReference')->findAll();
$references = $this->references;
if (count($savedReferences) <= 0){
$this->redirect();
}
for ($i =0; $i<count($savedReferences); $i++){
if ($savedReferences[$i] !== $references[$i]) {
$this->redirect();
}
}
}
public function redirect()
{
$request = $this->requestStack->getCurrentRequest();
$route = $request->get('_route');
$this->session->getFlashBag()->add('warning', 'You need to setup the references for the parameters before starting the work');
if ($route != 'references'){
$url = $this->router->generate('references');
$redirect = new RedirectResponse($url);
$redirect->send();
}
}
}
Here is services.yml
build.references:
class: AdminBundle\EventListener\BuildReferencesEventListener
arguments: ['#doctrine.orm.entity_manager', '#router', '#request_stack', '#session']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
Add argument inside your service.yml event listner service section
Your.service:
Class: AdminBundle\EventListener\BuildReferencesEventListener
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: kernel.event_listener, event: kernel.controller }
And add this __cunstruct code inside your listner:
namespace AdminBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Doctrine\ORM\EntityManager;
class BuildReferencesEventListener
{
protected $em;
function __construct(EntityManager $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
//TO DO call doctrine
// $em = Enity Manager...
// $em->getRepository...
// I hope its clear enough what im trying to do
}
In Symfony 3.4 and above, autowiring is enabled by default, so you just have to add a type-hinted argument to your service's constructor method and a private property to assign the argument's value to.
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class BuildReferencesEventListener
{
private $em;
public __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
$em = this->em;
// [...]
}
}
If you need to
declare arguments explicitly, you could do this in your service configuration:
# config/services.yaml
services:
# ... same code as before
# explicitly configure the service
AdminBundle\EventListener\BuildReferencesEventListener:
arguments:
$em: '#doctrine.orm.entity_manager_interface'
This could be useful to pass parameters that the service container doesn't know.
To learn more about the service container, https://symfony.com/doc/3.4/service_container.html#injecting-services-config-into-a-service.
Admin generate user then user first time login i want to redirect user to change password.
I have google but there are no any post found that make this fix.
Below i have attached service.yml and loginsuccessListener file code.
service.yml
services:
fos_user.security.interactive_login_listener:
class: OnlineCare\MarketingWebBundle\EventListener\LastLoginListener
tags:
- { name: kernel.event_subscriber, event: kernel.request, method: onKernelRequest }
arguments: ['#fos_user.user_manager','#router','#service_container']
LastLoginListiner.php
namespace OnlineCare\MarketingWebBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LastLoginListener implements EventSubscriberInterface
{
protected $userManager;
private $router;
private $container;
public function __construct(UserManagerInterface $userManager, UrlGeneratorInterface $router, ContainerInterface $container)
{
$this->userManager = $userManager;
$this->router = $router;
$this->container = $container;
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'onImplicitLogin',
SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
);
}
public function onImplicitLogin(UserEvent $event)
{
$user = $event->getUser();
$user->setLastLogin(new \DateTime());
$this->userManager->updateUser($user);
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if(empty($user->getLastLogin())){
$url = $this->router->generate('fos_user_change_password');
**return $event->setResponse(new RedirectResponse($url));**
} elseif ($user instanceof UserInterface) {
$user->setLastLogin(new \DateTime());
$this->userManager->updateUser($user);
}
}
}
I have a problem in the code of event listener because i need to make a EventListener when i login as type 1 ==>redirect to:
return $this->redirect($this->generateUrl('demands_patient'));
else redirect to another path .And this is the code of loginlistener
use Doctrine\ORM\Event\LifecycleEventArgs;
use Register\UserBundle\Entity\User;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener implements EventSubscriberInterface
{
private $router;
public function __construct(UrlGeneratorInterface $router)
{
$this->router = $router;
}
public static function onSecurityInteractiveLogin()
{
return array(
FOSUserEvents::AUTHENTICATION_SUCCESS => 'redirectlogin',
);
}
public function redirectlogin(InteractiveLoginEvent $event)
{
$user = $event->getUser();
if($user->getType()=="1")
{
return $this->redirect($this->generateUrl('demands_patient'));
}
else
{
return $this->redirect($this->generateUrl('demands'));
}
}
}
and i add configuration in services.yml:
register_user.eventlistener.loginlistener:
class: Register\UserBundle\EventListener\LoginListener
arguments: [#router]
tags:
- { name: kernel.onSecurityInteractiveLogin }
Just today I faced a similar problem.
My solution was to use a Handler instead of a Listener, as explained in http://www.reecefowell.com/2011/10/26/redirecting-on-loginlogout-in-symfony2-using-loginhandlers/
What I did is:
In my bundle's `services.yml':
parameters:
cambra_comunitats.handler.user_login_redirect_handler.class: Cambra\ComunitatsBundle\Handler\UserLoginRedirectHandler
services:
cambra_comunitats.handler.user_login_redirect_handler:
class: "%cambra_comunitats.handler.user_login_redirect_handler.class%"
arguments: [#router, #security.context]
tags:
- { name: 'monolog.logger', channel: 'security' }
Then, I created the Cambra\ComunitatsBundle\Handler\UserLoginRedirectHandler class:
class UserLoginRedirectHandler implements \Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
public function __construct(\Symfony\Component\Routing\Router $router, \Symfony\Component\Security\Core\SecurityContext $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(\Symfony\Component\HttpFoundation\Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token)
{
if ($this->security->isGranted('ROLE_ADMIN') && $this->security->getToken()->getUser()->isMustRedirect())
{
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($this->router->generate('new_destination'));
} else
{
$referer_url = $this->router->generate('index_page');
$response = new \Symfony\Component\HttpFoundation\RedirectResponse($referer_url);
}
return $response;
}
}
And, finally, I enabled it on security.yml, inside of firewalls.main.form_login:
//.....
firewalls:
//...
main:
//...
form_login:
//...
success_handler: cambra_comunitats.handler.user_login_redirect_handler