Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User from Security service is not available in Doctrine Listeners #34

Open
BastienLedon opened this issue Nov 9, 2020 · 10 comments
Open
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@BastienLedon
Copy link

Hello,

When trying to test a part of our code with an Event Listener like:

<?php
namespace App\EventListener\Doctrine;

use Symfony\Component\Security\Core\Security;

class Listener
{
    private Security $security;
    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    public function prePersist(Entity $entity)
    {
        $user = $this->security->getUser();
        $entity->setUser($user);
    }
}

$user is always null

I found this SO question with the exact same problem:
https://stackoverflow.com/questions/57609821/make-security-available-for-doctrine-onflush-within-functional-test

Do you recommend using the workaround given in the SO answers or something else ?

@TavoNiievez TavoNiievez self-assigned this Nov 28, 2020
@TavoNiievez
Copy link
Member

TavoNiievez commented Nov 28, 2020

Hey, @BastienLedon
Hmm, in my applications i have a code very similar to yours, but it works perfectly for me.

I don't think what those answers suggest is necessary.

However, there are several things that should be verified, the best way i could help you is in a real application.
Can you create a demo of your problem based on this repo?

You can choose a branch for the version of symfony you are using, and from there we can see what the real problem is.

@BastienLedon
Copy link
Author

BastienLedon commented Nov 30, 2020

@TavoNiievez
Of course, I think you can reproduce here, https://github.com/BastienLedon/codeception-symfony-tests

I've added one other user and just added the amLoggedAs inside those test. (And created a fake Listener)

You can see that inside the controller the user of Security is available but not inside the Listener.

@TavoNiievez
Copy link
Member

TavoNiievez commented Nov 30, 2020

Yep, i can confirm that there is unexpected behavior here.

The Security object requires the symfony container as a parameter.
For some reason in the tests the 'real' container: 'service_container' is injected instead of the test container: 'test.service_container'.

In fact, if you manually inject the test container everything works correctly:

    public function prePersist(LifecycleEventArgs $entity)
    {
        $user = $entity->getObject();
        if($user instanceof User) {
            /** @var ContainerInterface $testContainer */
            $testContainer = $this->container->get('test.service_container');
            $security = new Security($testContainer);
            $user->setUser($security->getUser());
        }
    }

I'm still not sure if it's Codeception behavior or Symfony itself.

But at least there should be documentation on this.

Lastly, I added an Entity Listener to the test project to be able to test this easily in the future.

@TavoNiievez
Copy link
Member

TavoNiievez commented Nov 30, 2020

In theory, a workaround would be to inject the correct service into the env-specific services config file: services_test.yaml.

@TavoNiievez TavoNiievez removed their assignment Dec 5, 2020
@TavoNiievez TavoNiievez added the bug Something isn't working label Dec 5, 2020
@ruslan-polutsygan
Copy link

Any news on this?
I have faced the same issue, spent whole day debugging my tests and I give up 😞

@TavoNiievez TavoNiievez added the help wanted Extra attention is needed label Apr 13, 2021
@ruslan-polutsygan
Copy link

ruslan-polutsygan commented Apr 14, 2021

Ok.
Problem is in wrong container being used for doctrine subscribers (Maybe for all persistent services? Didn't check)

Let's say there is a service.

<?php

namespace App\Service;

use App\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

class LocatorConsumerService implements ServiceSubscriberInterface
{
    /**
     * @var ContainerInterface
     */
    private $locator;

    public function __construct(ContainerInterface $locator)
    {
        $this->locator = $locator;
    }

    public function getUserFromLocator(): ?User
    {
        $token = $this->locator->get('security.token_storage')->getToken();
        $user = null;
        if($token) {
            $user = $token->getUser();
        }

        return $user;
    }

    public static function getSubscribedServices(): array
    {
        return [
            'security.token_storage' => TokenStorageInterface::class
        ];
    }

}

If service is injected into controller, user is logged in:

    public function my(LocatorConsumerService $service): Response
    {
// ... 
        $user = $service->getUserFromLocator(); // $user is not null
// ... 
    }

If service injected into any doctrine subscriber - $user is null

<?php

namespace App\EventSubscriber;

use App\Service\LocatorConsumerService;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;

class MySubscriber implements EventSubscriber
{
    /**
     * @var LocatorConsumerService
     */
    private $service;

    public function __construct(LocatorConsumerService $service) {
        $this->service = $service;
    }

    public function postLoad(LifecycleEventArgs $lifecycleEventArgs): void
    {
// ... 
        $user = $this->service->getUserFromLocator(); // $user is null here
// ... 
    }

    public function getSubscribedEvents(): array
    {
        return [
            Events::postLoad
        ];
    }
}

However, if I configure LocatorConsumerService manually in services_test.yaml like this:

    App\Service\LocatorConsumerService:
        arguments:
            - '@test.service_container'

$user is not null in both cases.

I can't figure out which service I have to re-declare this way though. And I'm not sure if such workaround can help at all

@ruslan-polutsygan
Copy link

@request_stack also empty from doctrine subscriber. Frustration.

@kolobok22
Copy link

same problem. no solution?

@godrar
Copy link

godrar commented Jan 4, 2022

Hello
My simple solution:

services_test.yaml

services:
    security.helper:
        class: Symfony\Component\Security\Core\Security
        arguments:
            - '@test.service_container'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants