Warm tip: This article is reproduced from stackoverflow.com, please click
api-platform.com codeception doctrine symfony symfony-security

Make security available for doctrine onFlush within functional test

发布于 2021-01-02 01:41:58

I'm actually testing my api code written with:

  • symfony 4
  • api-platform
  • FOS User
  • JWT

I use codeption for my tests and everything is ok so far.

For several entities, I fire onFlush doctrine callback and it's working just fine when authenticated from my front application in react.

At this point I get my authenticated user in the callback via an injected security component.

However when doing the same things via codeception, even if onFlush is fired, I'm not able to retrieve my user neither the token via the security injection.

I tried to inject the token instead, also the entire service container, none has worked.

This is my OnFlush class:

{
    /**
     * @var Security
     */
    private $security;

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

    public function onFlush(OnFlushEventArgs $args): void
    {
        $user = $this->security->getUser();
...

And here how I set my authorization header in codeception test:

$I->haveHttpHeader('Authorization', 'Bearer ' . $token);

$I->sendPUT(
  '/entity/uuid.json',
  [
    'attribute' => $value
  ]
);

I would like to get the user having the specified token whe executing the test in the callback.

PS: Before executing the PUT test, I did the same thing with GET and just got the related entities, when I remove Authorization header I do get all users entities. It seems that it's not working only in callback.

Thanks

Questioner
baderben
Viewed
0
baderben 2019-08-30 18:17

After a lot research, it's obviously a codeception problem.

I ended up making this particular test with phpunit as codeception couldn't load the service container in doctrine events.

If you try to edit your services.yaml file and to execute your tests, it works on first time as the service container is re-built (re-cached).

But once cached, it will always return an empty container (without tokenstrorage, security, ...).

Creating a helper method to provide the user wouldn't work neither, I'll leave the code here in case of need:

/**
     * Create user or administrator and set auth cookie to client
     *
     * @param string $user
     * @param string $password
     * @param bool $admin
     */
    public function setAuth(string $user, string $password, bool $admin = false): void
    {
        /** @var Symfony $symfony */
        try {
            $symfony = $this->getModule('Symfony');
        } catch (ModuleException $e) {
            $this->fail('Unable to get module \'Symfony\'');
        }
        /** @var Doctrine2 $doctrine */
        try {
            $doctrine = $this->getModule('Doctrine2');
        } catch (ModuleException $e) {
            $this->fail('Unable to get module \'Doctrine2\'');
        }

        $user = $doctrine->grabEntityFromRepository(User::class, [
            'username' => $user
        ]);

        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $symfony->grabService('security.token_storage')->setToken($token);
        /** @var Session $session */
        $session = $symfony->grabService('session');
        $session->set('_security_main', serialize($token));
        $session->save();
        $cookie = new Cookie($session->getName(), $session->getId());
        $symfony->client->getCookieJar()->set($cookie);
    }

Creating a phpunit test with below code would do the job just fine:

/**
     * @param string $method
     * @param string $url
     * @param array $content
     * @param bool $authorization
     */
    protected static function performRequest (string $method, string $url, array $content = [], $authorization = false): void
    {
        $headers = [
            'CONTENT_TYPE' => 'application/json',
        ];

        if ($authorization)
        {
            $headers = array_merge($headers, [
                'HTTP_AUTHORIZATION' => 'Bearer ' . self::$token
            ]);
        }

        self::$client->request(
            $method,
            '/api/' . $url,
            [],
            [],
            $headers,
            json_encode($content)
        );
    }