Symfony 3 get current user inside entity - orm

I was wondering if there is a way that i can initialize the property owner with an entity User of FOSUserBundle so that it contains the user who created the Post
I want to do this inside the constructor as shown below.
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{
/* here are defined some attributs */
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="posts")
* #ORM\JoinColumn(name="owner", referencedColumnName="id")
*/
private $owner;
public function __construct()
{
$this->owner = /* get current user */ ;
}
}
Is there a way to do this by replacing the comment in the constructor with something ?
Thank you for your answers

No, there isn't. [*]
There are at least two ways to deal with this:
Create your Post entities through a factory service which populates the
owner property:
namespace My\Bundle\EntityFactory;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use My\Bundle\Entity\Post;
class PostFactory
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function createPost()
{
$user = $this->tokenStorage()->getToken()->getUser();
$post = new Post($user);
}
}
(for this example, you will have to modify your Post constructor to
accept the owner as a parameter)
In services.yml:
services:
post_factory:
class: My\Bundle\EntityFactory\PostFactory
arguments: [#security.token_storage]
To create an entity from your controller:
$post = $this->container->get('post_factory')->createPost();
If you can tolerate that the owner will only be set once you persist the
entity, you can use a doctrine event listener:
namespace My\Bundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use My\Bundle\Entity\Post;
class PostOwnerAssignmentListener
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(LifecycleEventArgs $event)
{
$entity = $event->getEntity();
if ($entity instanceof Post && !$entity->getOwner()) {
$entity->setOwner($this->tokenStorage->getToken()->getUser());
}
}
}
In services.yml:
services:
post_owner_assignment_listener:
class: My\Bundle\EventListener\PostOwnerAssignmentListener
arguments: [#security.token_storage]
tags:
- { name: doctrine.event_listener, event: prePersit }
The advantage here is that the owner gets assigned no matter how and where
the Post is created.
[*]: Well, technically with the default app.php you could access the
kernel by declaring global $kernel; in your constructor and go from there,
however this is very strongly discouraged and may break in strange and subtle
ways.

I think you are way over-complicating this issue. When you create a new Post in your controller, either in the controller or in the repository do something like this:
use AppBundle\Entity\Post; //at top of controller
$em = $this->getDoctrine()->getManager();
$user = $this->container->get('security.token_storage')->getToken()->getUser();
$post = new Post();
$em->persist( $post );
$post->setOwner( $user );
// set other fields in your post entity
$em->flush();

For Symfony 4+ with Autowiring and Entity event listener:
In /EventListener/PostPrePersistListener.php:
namespace App\EventListener;
use App\Entity\Post;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class PostPrePersistListener
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(Post $post, LifecycleEventArgs $event)
{
$post->setOwner($this->tokenStorage->getToken()->getUser());
}
}
In services.yaml:
services:
App\EventListener\PostPrePersistListener:
autowire: true
tags:
- { name: doctrine.orm.entity_listener, entity: 'App\Entity\Post', event: prePersist }
Modifying services.yaml is required as Symfony cannot know that this custom service is tagged to hook on doctrine.event_listener
This works at Entity-level as asked, to ensure Controller do not handle the owner value.

Related

Why is my User Login no longer working after upgrading to Symfony3

I came along a strange Problem with Symfony 3.
Under Symfony 2 everyhting worked out of the Box (Login).
But under Symfony 3 it doesn't validate at all.
The Doctrine Layer is not Loading my User Object nor the Repository.
Whats going on?
UserProviderInterface was changed to UserLoaderInterface in 2.8 (see doc)
class UserRepository extends EntityRepository implements
UserProviderInterface
class UserRepository extends EntityRepository
implements UserLoaderInterface
This wil fix the problem, you can also delete these functions:
public function refreshUser(UserInterface $user)
public function supportsClass($class)
Ok, short update.
I was able to fix this and would like to share what happened.
After Debuging the complete Login Prozess I stumbled accross the main cause for not beeing able to login.
<?php
// src/AppBundle/Entity/UserRepository.php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
$user = $this->createQueryBuilder('u')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
if (null === $user) {
$message = sprintf(
'Unable to find an active admin AppBundle:User object identified by "%s".',
$username
);
throw new UsernameNotFoundException($message);
}
return $user;
}
public function refreshUser(UserInterface $user)
{
$class = get_class($user);
if (!$this->supportsClass($class)) {
throw new UnsupportedUserException(
sprintf(
'Instances of "%s" are not supported.',
$class
)
);
}
return $this->find($user->getId());
}
public function supportsClass($class)
{
return $this->getEntityName() === $class
|| is_subclass_of($class, $this->getEntityName());
}
}
Ok,
this Repository Query Class is actually the reason why it is not working.
After Debuging and Testing I came Along this Code Block in the Class:
Symfony\Bridge\Doctrine\Security\User
/**
* {#inheritdoc}
*/
public function loadUserByUsername($username)
{
if (null !== $this->property) {
$user = $this->repository->findOneBy(array($this->property => $username));
} else {
if (!$this->repository instanceof UserLoaderInterface) {
throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface.', get_class($this->repository)));
}
$user = $this->repository->loadUserByUsername($username);
}
if (null === $user) {
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
return $user;
}
It states that the Repository Class must be an instance of UserLoaderInterface.
But the Documentation from
http://symfony.com/doc/current/cookbook/security/entity_provider.html
states it is an Instance of UserProviderInterface.
so the Login fails as it is not the right Interface implemented.
The Documentation (Cookbook) has an old Information in it, or the Symfony Team just simply forgot about it. ^^(can happen)
Hope this helps someone ^^
For me the issue was that the isEqualTo method of the EquatableInterface (on my User entity) was returning false when it should have been returning true.

Zend Framework 2 - Service method require as parameter InputFilter

I have a bit OOD question.
I have service:
namespace Front\Service\Course;
use Front\ORM\EntityManagerAwareInterface;
use Zend\Http\Request;
use Zend\InputFilter\InputFilter;
use Front\InputFilter\Course\CreateFilter;
class Create implements EntityManagerAwareInterface
{
/**
* #var \Doctrine\Orm\EntityManager
*/
protected $entityManager = null;
public function create(CreateFilter $createFilter)
{
if (!$createFilter->isValid()) return false;
/* #var $courseRepository \Front\Repositories\CourseRepository */
$courseRepository = $this->getEntityManager()->getRepository('Front\Entities\Course');
$course = $courseRepository->findByName($createFilter->getCourse());
}
/* (non-PHPdoc)
* #see \Front\ORM\EntityManagerAwareInterface::getEntityManager()
*/
public function getEntityManager()
{
return $this->entityManager;
}
/* (non-PHPdoc)
* #see \Front\ORM\EntityManagerAwareInterface::setEntityManager()
*/
public function setEntityManager(\Doctrine\ORM\EntityManager $entityManager)
{
$this->entityManager = $entityManager;
return $this;
}
}
And controller :
class CreateController extends \Zend\Mvc\Controller\AbstractController
{
public function onDispatch(MvcEvent $e)
{
$jsonModel = new JsonModel();
/* #var $courseCreateService \Front\Service\Course\Create */
$courseCreateService = $this->getServiceLocator()->get('Front\Service\Course\Create');
$courseCreateFilter = new CreateFilter();
$courseCreateFilter->setData($this->params()->fromPost());
if (!$courseCreateFilter->isValid()) {
$jsonModel->setVariable('status', 0);
$jsonModel->setVariable('message', $courseCreateFilter->getMessages());
return;
}
$courseCreateService->create($courseCreateFilter);
}
}
By service method declaration :
public function create(CreateFilter $createFilter)
i force user of the Service to use CreateFilter container which derived from Zend/InputFilter every time when he want to create new Course.
My question is: Might it be better when i will send to the service layer not the Typed object but simple value?
On example in my case it is might looks like:
public function create($courseName)
My CreateFilter looks like:
class CreateFilter extends InputFilter
{
public function __construct()
{
$input = new Input('name');
$validatorChain = new ValidatorChain();
$validatorChain->addValidator(new StringLength(array('max'=>60)))
->addValidator(new NotEmpty());
$input->setRequired(true)->setValidatorChain($validatorChain);
$this->add($input);
}
/**
* #return string | null
*/
public function getCourse()
{
return $this->getValue('name');
}
}
If you provide a concrete class name as you're doing now, you're forever tied to a concrete implementation of the class or one derived from it. If you decide later that you want to use a different class entirely, you have to refactor your service class code, whereas with an interface, you only need to implement it in your new class and your service will continue to work without any changes.
Without any interface at all, your service class would have to do extra checks to first see if it's an object and then if it implements the method you're expecting before it can even begin doing its job. By requiring an interface you remove the uncertainty, and negate the need for checks.
By providing an interface you create a contract between your methods and the classes they're expecting as arguments without restricting which classes may enter into the contract. All in all, contract by interface is preferable to contract by class name, but both are preferable to no contract at all.
I usually bind my entities to my form, so they are populated with the data from the form. This way, you inject the entity to your service and imho that's much cleaner. The service should not be aware of how you got your data.
My "admin" controller for an entity Bar usually is injected with three objects: the repository (to query objects), the service (to persist/update/delete objects) and the form (to modify objects for the user). A standard controller is then very CRUD based and only pushes entities to the service layer:
<?php
namespace Foo\Controller;
use Foo\Repository\Bar as Repository;
use Foo\Form\Bar as Form;
use Foo\Service\Bar as Service;
use Foo\Entity\Bar as Entity;
use Foo\Options\ModuleOptions;
use Zend\Mvc\Controller\AbstractActionController;
class BarController extends AbstractActionController
{
/**
* #var Repository
*/
protected $repository;
/**
* #var Service
*/
protected $service;
/**
* #var Form
*/
protected $form;
/**
* #var ModuleOptions
*/
protected $options;
public function __construct(Repository $repository, Service $service, Form $form, ModuleOptions $options = null)
{
$this->repository = $repository;
$this->service = $service;
$this->form = $form;
if (null !== $options) {
$this->options = $options;
}
}
public function getService()
{
return $this->service;
}
public function getRepository()
{
return $this->repository;
}
public function getForm()
{
return $this->form;
}
public function getOptions()
{
if (null === $this->options) {
$this->options = new ModuleOptions;
}
return $this->options;
}
public function indexAction()
{
$bars = $this->getRepository()->findAll();
return array(
'bars' => $bars,
);
}
public function viewAction()
{
$bar = $this->getBar();
return array(
'bar' => $bar,
);
}
public function createAction()
{
$bar = $this->getBar(true);
$form = $this->getForm();
$form->bind($bar);
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost();
$form->setData($data);
if ($form->isValid()) {
// Bar is populated with form data
$this->getService()->create($bar);
return $this->redirect()->toRoute('bar/view', array(
'bar' => $bar->getId(),
));
}
}
return array(
'form' => $form,
);
}
public function updateAction()
{
$bar = $this->getBar();
$form = $this->getForm();
$form->bind($bar);
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost();
$form->setData($data);
if ($form->isValid()) {
$this->getService()->update($bar);
return $this->redirect()->toRoute('bar/view', array(
'bar' => $bar->getId(),
));
}
}
return array(
'bar' => $bar,
'form' => $form,
);
}
public function deleteAction()
{
if (!$this->getRequest()->isPost()) {
$this->getRequest()->setStatusCode(404);
return;
}
$bar = $this->getBar();
$this->getService()->delete($bar);
return $this->redirect()->toRoute('bar');
}
protected function getBar($create = false)
{
if (true === $create) {
$bar = new Entity;
return $bar;
}
$id = $this->params('bar');
$bar = $this->getRepository()->find($id);
if (null === $bar) {
throw new Exception\BarNotFoundException(sprintf(
'Bar with id "%s" not found', $id
));
}
return $bar;
}
}
I made a gist file on Github with this full code (it's better readable) and the service. The service relies on the interface, so you can even swap out the entity object by another one having the same interface.
Check the full thing out here: https://gist.github.com/juriansluiman/5472787
Thanks all for answering, owing to answers and analyzing, i have reached conclusion which most applicable for my situation. I agree that Service in my case should not wait concrete object, it is should wait an abstraction with getCourse method.
And i completely agree with "Crisp" answer:
All in all, contract by interface is preferable to contract by class name, but both are preferable to no contract at all.
So i need to extract Interface with one method
getCourse
or
getName
, and remove
if (!$createFilter->isValid()) return false;
so Interface:
interface CourseInterface
{
/**
* #return String
**/
public function getName();
}
and Service:
class Create implements EntityManagerAwareInterface
{
/**
* #var \Doctrine\Orm\EntityManager
*/
protected $entityManager = null;
/**
* #param CourseInterface $course
* #param UserInterface $creator
*/
public function create(CourseInterface $course)
{
$courseEntity = new Course();
$courseEntity->setName($course->getName());
$this->entityManager->persist($courseEntity);
$this->entityManager->flush();
.....
Thanks all.

Yii Framework - from url to route

I searched, but couldnt find something.
So, I have route rules:
...
'/reg' => '/user/user/registration',
...
in
Yii::app()->request
I couldn find any route information.
So, how can I get in module init function and having only url, route lile
/reg -> user/user/registration
UPD
The route is only available from the running controller. By the time when a module is initialized the controller is not yet available, thus you can't find out the route there. (You can follow CWebApplication::processRequest to see what happens when a request is resolved up to the point where the controller is run.)
It depends on what you try to achieve, but you could override WebModule::beforeControllerAction to do something before the module controller is run.
Today (next day after my question), I could solve this.
I will try to explain:
As Michael wrote, we cant know in module in which controller we are.
But I net get just reversed route, so, its quite esay.
Yii::app()->getUrlManager()->parseUrl('/reg');
This will return my reversed route
user/user/registration
parseUrl
Solution for Yii 1.1.15 workes for me.
class HttpRequest extends CHttpRequest {
protected $_requestUri;
protected $_pathInfo;
public function setUri($uri){
$this->_requestUri = $uri;
}
public function setPathInfo($route){
$this->_pathInfo = $route;
}
public function getPathInfo(){
/* copy from parent */
}
public function getRequestUri(){
/* copy from parent */
}
}
The usage:
$uri_path = 'my/project-alias/wall';
/** #var HttpRequest $request */
$request = clone Yii::app()->getRequest();
$request->setUri($uri_path);
$request->setPathInfo(null);
$route = Yii::app()->getUrlManager()->parseUrl($request);
//$route equals 'project/profile/wall' etc here (like in route rules);
I'm using a slightly different sub-class of CHttpRequest:
class CustomHttpRequest extends \CHttpRequest
{
/**
* #var string
*/
var $pathInfo;
/**
* #var string
*/
private $method;
public function __construct($pathInfo, $method)
{
$this->pathInfo = $pathInfo;
$this->method = $method;
}
public function getPathInfo()
{
return $this->pathInfo; // Return our path info rather than the default
}
public function getRequestType()
{
return $this->method;
}
}
Then to call it (to create a controller, which is what I want):
$request = new CustomHttpRequest($uri, $method); // e.g. 'my/project-alias/wall' and 'GET'
$route = \Yii::app()->getUrlManager()->parseUrl($request);
list($jcontroller, $actionName) = \Yii::app()->createController($route);

Load a listener before the security or custom provider

I'm working on a custom provider that works exactly like a classical user form, however I have to give a second parameter to identify the user: a websiteId (I'm creating a dynamic website plateform).
So a username is no more unique, but the combinaison of username and websiteId it is.
I successfully created my custom authentication, the last problem I have is to get the websiteId from the domain thanks to a listener, it works, but infortunately the method that get the website id from the domain is loaded after my authentication provider, so I can't get the websiteId in time :(
I tried to change the listener priority (test 9999, 1024, 255 and 0, and negative numbers -9999, -1024, -255 etc...), in vain, it's loaded always after.
Here my code:
services.yml:
services:
# Listeners _________________
website_listener:
class: Sybio\Bundle\WebsiteBundle\Services\Listener\WebsiteListener
arguments:
- #doctrine
- #sybio.website_manager
- #translator
- %sybio.states%
tags:
- { name: kernel.event_listener, event: kernel.request, method: onDomainParse, priority: 255 }
# Security _________________
sybio_website.user_provider:
class: Sybio\Bundle\WebsiteBundle\Security\Authentication\Provider\WebsiteUserProvider
arguments: [#website_listener, #doctrine.orm.entity_manager]
My listener is "website_listener", and you can see i use it for my sybio_website.user_provider as argument.
WebsiteListener:
// ...
class WebsiteListener extends Controller
{
protected $doctrine;
protected $websiteManager;
protected $translator;
protected $websiteId;
/**
* #var array
*/
protected $entityStates;
public function __construct($doctrine, $websiteManager, $translator, $entityStates)
{
$this->doctrine = $doctrine;
$this->websiteManager = $websiteManager;
$this->translator = $translator;
$this->entityStates = $entityStates;
}
/**
* #param Event $event
*/
public function onDomainParse(Event $event)
{
$request = $event->getRequest();
$website = $this->websiteManager->findOne(array(
'domain' => $request->getHost(),
'state' => $this->entityStates['website']['activated'],
));
if (!$website) {
throw $this->createNotFoundException($this->translator->trans('page.not.found'));
}
$this->websiteId = $website->getId();
}
/**
* #param integer $websiteId
*/
public function getWebsiteId()
{
return $this->websiteId;
}
}
$websiteId is hydrated, not in time as you will see in my provider...
WebsiteUserProvider:
<?php
namespace Sybio\Bundle\WebsiteBundle\Security\Authentication\Provider;
// ...
class WebsiteUserProvider implements UserProviderInterface
{
private $em;
private $websiteId;
private $userEntity;
public function __construct($websiteListener, EntityManager $em)
{
$this->em = $em;
$this->websiteId = $websiteListener->getWebsiteId(); // Try to get the website id from my listener, but it's method onDomainParse is not called in time
$this->userEntity = 'Sybio\Bundle\CoreBundle\Entity\User';
}
public function loadUserByUsername($username)
{
// I need the websiteId here to identify the user by its username and the website:
if ($user = $this->findUserBy(array('username' => $username, 'website' => $this->websiteId))) {
return $user;
}
throw new UsernameNotFoundException(sprintf('No record found for user %s', $username));
}
// ...
}
So any idea will be appreciate ;)
I spent a lot of time to set up my authentication configuration, but now I can't get the websiteId in time, too bad :(
Thanks for your anwsers !
EDIT:
I had also other files of my authentication system to understand, I don't think I can control the provider position when loading, because they're witten in the security.yml config:
WebsiteAuthenticationProvider:
// ...
class WebsiteAuthenticationProvider extends UserAuthenticationProvider
{
private $encoderFactory;
private $userProvider;
/**
* #param \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider
* #param UserCheckerInterface $userChecker
* #param $providerKey
* #param EncoderFactoryInterface $encoderFactory
* #param bool $hideUserNotFoundExceptions
*/
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true)
{
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
$this->encoderFactory = $encoderFactory;
$this->userProvider = $userProvider;
}
/**
* {#inheritdoc}
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
$user = $token->getUser();
if ($user instanceof UserInterface) {
return $user;
}
try {
$user = $this->userProvider->loadUserByUsername($username);
if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
}
return $user;
} catch (UsernameNotFoundException $notFound) {
throw $notFound;
} catch (\Exception $repositoryProblem) {
throw new AuthenticationServiceException($repositoryProblem->getMessage(), $token, 0, $repositoryProblem);
}
}
// ...
}
The factory:
// ...
class WebsiteFactory extends FormLoginFactory
{
public function getKey()
{
return 'website_form_login';
}
protected function getListenerId()
{
return 'security.authentication.listener.form';
}
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'security.authentication_provider.sybio_website.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication_provider.sybio_website'))
->replaceArgument(0, new Reference($userProviderId))
->replaceArgument(2, $id)
;
return $provider;
}
}
SybioWebsiteBundle (dependency):
// ...
class SybioWebsiteBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new WebsiteFactory());
}
}
Security:
security:
firewalls:
main:
provider: website_provider
pattern: ^/
anonymous: ~
website_form_login:
login_path: /login.html
check_path: /login
logout:
path: /logout.html
target: /
providers:
website_provider:
id: sybio_website.user_provider
Firewall::onKernelRequest is registered with a priority of 8 (sf2.2). A priority of 9 should ensure that your listener is called first (works for me).
I had a similar problem, which was to create subdomain-specific "Campaign" sites within a single sf2.2 app: {campaign}.{domain} . Every User has many Campaigns and I, like you, wanted to prevent a User without the given Campaign from logging in.
My solution was to create a Doctrine filter to add my campaign criteria to every relevant query made under {campaign}.{domain}. A kernel.request listener (with priority 9!) is responsible for activating the filter before my generic user provider tries to loadUserByUsername. I use mongodb, but the idea is similar for ORM.
The best part is that I'm still using stock authentication classes. This is basically all there is to it:
config.yml:
doctrine_mongodb:
document_managers:
default:
filters:
campaign:
class: My\Filter\CampaignFilter
enabled: false
CampaignFilter.php:
class CampaignFilter extends BsonFilter
{
public function addFilterCriteria(ClassMetadata $targetMetadata)
{
$class = $targetMetadata->name;
$campaign = $this->parameters['campaign'];
$campaign = $campaign instanceof Campaign ? $campaign->getId() : $campaign;
if ($targetMetadata->hasField('campaign')) {
return array('campaign' => $this->parameters['campaign']);
}
if ($targetMetadata->hasField('campaigns')) {
return array('campaigns' => $this->parameters['campaign']);
}
return array();
}
}
My listener is declared as:
<service id="my.campaign_listener" class="My\EventListener\CampaignListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="9" />
<argument type="service" id="doctrine.odm.mongodb.document_manager" />
</service>
The listener class:
class CampaignListener
{
private $dm;
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST != $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if ($campaign = $request->attributes->get('campaign', false)) {
$filters = $this->dm->getFilterCollection();
$filter = $filters->enable('campaign');
$filter->setParameter('campaign', $campaign);
}
}
}
'campaign' is available in the request here thanks to my routing configuration:
campaign:
resource: "#My/Controller/CampaignController.php"
type: annotation
host: "{campaign}.{domain}"
defaults:
campaign: test
domain: %domain%
requirements:
domain: %domain%
.. and %domain% is a parameter from config.yml or config_dev.yml
Like the response provide by benki07 it's a question of prority, you have to put your listener before the Firewall::onKernelRequest
Then, your listener will be called -> Firewall is call and your authentification listener are called with the webSiteId registered.
As you can see in the SecurityExtension.php The factories used do not have any sort of priority system. It just adds your factory to the end of the array, that's it.
Therefore it is impossible to put your custom authentication before that of symfony's security component.
An option may be to override the DaoAuthenticationProvider class paramater with your class. I hope that symfony2 will change from factories to a registry where you can add your custom authentication with a tag and a priority because this is not open/closed enough for me.

Access doctrine from authentication failure handler in Symfony2

I'm trying to write some loggin failure info in database from a custom authentication handler.
My problem is to gain access to the database since I don't know where the Doctrine object might be stored
Here's my code for now :
namespace MyApp\FrontBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request as Request;
use Symfony\Component\HttpFoundation\RedirectResponse as RedirectResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Security\Http\Authentication as Auth;
use Symfony\Component\Security\Core\Exception\AuthenticationException as AuthException;
class SecurityHandler implements Auth\AuthenticationFailureHandlerInterface
{
public function onAuthenticationFailure(Request $request, AuthException $token)
{
try
{
$lastLoginFailure = new DateTime();
// get database object here
}
catch(\Exception $ex)
{
}
}
}
Any ideas ?
Turn your SecurityHandler into a service and then inject the doctrine entity manager into it.
http://symfony.com/doc/current/book/service_container.html
Start command php app/console container:debug.
Copy doctrine.orm.entity_manager and paste to your hadler constructor arguments like
[...., #doctrine.orm.entity_manager].
In hadler use Doctrine\ORM\EntityManager;
I think you should extends your class "SecurityHandler" with ContainerAware if you want to use service since your Security Handler is not a controller.
class SecurityHandler extend ContainerAware implements Auth\AuthenticationFailureHandlerInterface{
public function onAuthenticationFailure(Request $request, AuthException $token)
{
try
{
$lastLoginFailure = new DateTime();
// get database object here
$doctrine = $this->container->get('doctrine');
$repository = $doctrine->getRepository('*NAME OF REPO*');
}
catch(\Exception $ex)
{
}
}
}