在功能测试中确保 onFlush 的安全性可用

问题描述 投票:0回答:2

我实际上正在测试我的 api 代码:

  • 交响乐4
  • api平台
  • FOS 用户
  • 智威汤逊

我使用 codeption 进行测试,到目前为止一切正常。

对于几个实体,我触发了 onFlush 原则回调,并且当从我的前端应用程序在 React 中进行身份验证时,它工作得很好。

此时,我通过注入的安全组件在回调中获取经过身份验证的用户。

但是,当通过代码接收执行相同的操作时,即使 onFlush 被触发,我也无法通过安全注入检索我的用户和令牌。

我尝试注入令牌,还有整个服务容器,但没有成功。

这是我的 OnFlush 课程:

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

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

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

这是我如何在代码接收测试中设置授权标头:

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

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

我想在回调中执行测试时让用户拥有指定的令牌。

PS:在执行 PUT 测试之前,我使用 GET 做了同样的事情,只获取了相关实体,当我删除 Authorization 标头时,我确实获取了所有用户实体。看来它不仅仅在回调中起作用。

谢谢

php symfony doctrine api-platform.com codeception
2个回答
0
投票

经过大量研究,这显然是一个代码接收问题。

我最终使用 phpunit 进行了这个特定的测试,因为代码接收无法在学说事件中加载服务容器。

如果您尝试编辑 services.yaml 文件并执行测试,它会在服务容器重新构建(重新缓存)时首次运行。

但是一旦缓存,它将始终返回一个空容器(没有令牌存储、安全性......)。

创建一个帮助器方法来提供用户也不起作用,我将在需要时将代码留在这里:

/**
     * 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);
    }

使用下面的代码创建 phpunit 测试就可以很好地完成工作:

/**
     * @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)
        );
    }

0
投票

我遇到了完全相同的问题。这是我的解决方案:

<?php

use Codeception\Stub;
use Codeception\Module\Doctrine2;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\Security;

class YourEventSubscriberCest
{
    /**
     * @var Security
     */
    protected Security $security;

    /**
     * @param FunctionalTester $I
     */
    public function _before(FunctionalTester $I, Doctrine2 $doctrine2)
    {
        $user = new User();
        $security = Stub::makeEmpty(Security::class, [
            'getUser' => $user,
        ]);
        $backup = $this->replaceSecurity($doctrine2, $security);
        if ($backup instanceof Security) {
            $this->security = $backup;
        }
    }

    public function _after(FunctionalTester $I, Doctrine2 $doctrine2)
    {
        $this->replaceSecurity($doctrine2, $this->security);
    }

    protected function replaceSecurity(Doctrine2 $doctrine2, object $newSecurity): ?object
    {
        $listeners = $doctrine2->_getEntityManager()->getEventManager()->getListeners('onFlush');
        foreach ($listeners as $listener) {
            if ($listener instanceof YourEventSubscriber) {
                $reflection = new \ReflectionObject($listener);
                $property = $reflection->getProperty('security');
                $property->setAccessible(true);
                $oldSecurity = $property->getValue($listener);
                $property->setValue($listener, $newSecurity);
                return $oldSecurity;
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.