feature #35997 [DX][Testing] Added a loginUser() method to test protected resources (javiereguiluz, wouterj)
This PR was merged into the 5.1-dev branch. Discussion ---------- [DX][Testing] Added a loginUser() method to test protected resources | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #26839 | License | MIT | Doc PR | tbd This finishes https://github.com/symfony/symfony/pull/32850 original description: > I know this won't work for 100% of our users ... but the goal is to make life easier to *most* of them. Thanks! A custom `ConcreteToken` test-object is created as suggested by @linaori, to not bind this token to any specific implementation (as other implementations aren't fully compatible with eachother). Commits -------2980a680d4
Added special test token and implemented 'real' functional testsf516829d99
[DX][Testing] Added a loginUser() method to test protected resources
This commit is contained in:
commit
a51a0c5bd5
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace Symfony\Bundle\FrameworkBundle;
|
namespace Symfony\Bundle\FrameworkBundle;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\TestBrowserToken;
|
||||||
|
use Symfony\Component\BrowserKit\Cookie;
|
||||||
use Symfony\Component\BrowserKit\CookieJar;
|
use Symfony\Component\BrowserKit\CookieJar;
|
||||||
use Symfony\Component\BrowserKit\History;
|
use Symfony\Component\BrowserKit\History;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
@ -19,6 +21,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||||
use Symfony\Component\HttpKernel\KernelInterface;
|
use Symfony\Component\HttpKernel\KernelInterface;
|
||||||
use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile;
|
use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates a browser and makes requests to a Kernel object.
|
* Simulates a browser and makes requests to a Kernel object.
|
||||||
|
@ -104,6 +107,31 @@ class KernelBrowser extends HttpKernelBrowser
|
||||||
$this->reboot = true;
|
$this->reboot = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserInterface $user
|
||||||
|
*/
|
||||||
|
public function loginUser($user, string $firewallContext = 'main'): self
|
||||||
|
{
|
||||||
|
if (!interface_exists(UserInterface::class)) {
|
||||||
|
throw new \LogicException(sprintf('"%s" requires symfony/security-core to be installed.', __METHOD__));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user instanceof UserInterface) {
|
||||||
|
throw new \LogicException(sprintf('The first argument of "%s" must be instance of "%s", "%s" provided.', __METHOD__, UserInterface::class, \is_object($user) ? \get_class($user) : \gettype($user)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = new TestBrowserToken($user->getRoles(), $user);
|
||||||
|
$token->setAuthenticated(true);
|
||||||
|
$session = $this->getContainer()->get('session');
|
||||||
|
$session->set('_security_'.$firewallContext, serialize($token));
|
||||||
|
$session->save();
|
||||||
|
|
||||||
|
$cookie = new Cookie($session->getName(), $session->getId());
|
||||||
|
$this->getCookieJar()->set($cookie);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\FrameworkBundle\Test;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A very limited token that is used to login in tests using the KernelBrowser.
|
||||||
|
*
|
||||||
|
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||||
|
*/
|
||||||
|
class TestBrowserToken extends AbstractToken
|
||||||
|
{
|
||||||
|
public function __construct(array $roles = [], UserInterface $user = null)
|
||||||
|
{
|
||||||
|
parent::__construct($roles);
|
||||||
|
|
||||||
|
if (null !== $user) {
|
||||||
|
$this->setUser($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCredentials()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class SecurityController implements ContainerAwareInterface
|
||||||
|
{
|
||||||
|
use ContainerAwareTrait;
|
||||||
|
|
||||||
|
public function profileAction()
|
||||||
|
{
|
||||||
|
return new Response('Welcome '.$this->container->get('security.token_storage')->getToken()->getUsername().'!');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\User\User;
|
||||||
|
|
||||||
|
class SecurityTest extends AbstractWebTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider getUsers
|
||||||
|
*/
|
||||||
|
public function testLoginUser(string $username, array $roles, ?string $firewallContext)
|
||||||
|
{
|
||||||
|
$user = new User($username, 'the-password', $roles);
|
||||||
|
$client = $this->createClient(['test_case' => 'Security', 'root_config' => 'config.yml']);
|
||||||
|
|
||||||
|
if (null === $firewallContext) {
|
||||||
|
$client->loginUser($user);
|
||||||
|
} else {
|
||||||
|
$client->loginUser($user, $firewallContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client->request('GET', '/'.($firewallContext ?? 'main').'/user_profile');
|
||||||
|
$this->assertEquals('Welcome '.$username.'!', $client->getResponse()->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsers()
|
||||||
|
{
|
||||||
|
yield ['the-username', ['ROLE_FOO'], null];
|
||||||
|
yield ['the-username', ['ROLE_FOO'], 'main'];
|
||||||
|
yield ['other-username', ['ROLE_FOO'], 'custom'];
|
||||||
|
|
||||||
|
yield ['the-username', ['ROLE_FOO'], null];
|
||||||
|
yield ['no-role-username', [], null];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoginUserMultipleRequests()
|
||||||
|
{
|
||||||
|
$user = new User('the-username', 'the-password', ['ROLE_FOO']);
|
||||||
|
$client = $this->createClient(['test_case' => 'Security', 'root_config' => 'config.yml']);
|
||||||
|
$client->loginUser($user);
|
||||||
|
|
||||||
|
$client->request('GET', '/main/user_profile');
|
||||||
|
$this->assertEquals('Welcome the-username!', $client->getResponse()->getContent());
|
||||||
|
|
||||||
|
$client->request('GET', '/main/user_profile');
|
||||||
|
$this->assertEquals('Welcome the-username!', $client->getResponse()->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoginInBetweenRequests()
|
||||||
|
{
|
||||||
|
$user = new User('the-username', 'the-password', ['ROLE_FOO']);
|
||||||
|
$client = $this->createClient(['test_case' => 'Security', 'root_config' => 'config.yml']);
|
||||||
|
|
||||||
|
$client->request('GET', '/main/user_profile');
|
||||||
|
$this->assertTrue($client->getResponse()->isRedirect('http://localhost/login'));
|
||||||
|
|
||||||
|
$client->loginUser($user);
|
||||||
|
|
||||||
|
$client->request('GET', '/main/user_profile');
|
||||||
|
$this->assertEquals('Welcome the-username!', $client->getResponse()->getContent());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle;
|
||||||
|
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||||
|
|
||||||
|
return [
|
||||||
|
new FrameworkBundle(),
|
||||||
|
new SecurityBundle(),
|
||||||
|
new TestBundle(),
|
||||||
|
];
|
|
@ -0,0 +1,26 @@
|
||||||
|
imports:
|
||||||
|
- { resource: ./../config/default.yml }
|
||||||
|
|
||||||
|
security:
|
||||||
|
providers:
|
||||||
|
main:
|
||||||
|
memory:
|
||||||
|
users:
|
||||||
|
the-username: { password: the-password, roles: ['ROLE_FOO'] }
|
||||||
|
no-role-username: { password: the-password, roles: [] }
|
||||||
|
custom:
|
||||||
|
memory:
|
||||||
|
users:
|
||||||
|
other-username: { password: the-password, roles: ['ROLE_FOO'] }
|
||||||
|
|
||||||
|
firewalls:
|
||||||
|
main:
|
||||||
|
pattern: ^/main
|
||||||
|
form_login:
|
||||||
|
check_path: /main/login/check
|
||||||
|
provider: main
|
||||||
|
custom:
|
||||||
|
pattern: ^/custom
|
||||||
|
form_login:
|
||||||
|
check_path: /custom/login/check
|
||||||
|
provider: custom
|
|
@ -0,0 +1,7 @@
|
||||||
|
security_profile:
|
||||||
|
path: /main/user_profile
|
||||||
|
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SecurityController::profileAction }
|
||||||
|
|
||||||
|
security_custom_profile:
|
||||||
|
path: /custom/user_profile
|
||||||
|
defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SecurityController::profileAction }
|
|
@ -47,6 +47,7 @@
|
||||||
"symfony/messenger": "^4.4|^5.0",
|
"symfony/messenger": "^4.4|^5.0",
|
||||||
"symfony/mime": "^4.4|^5.0",
|
"symfony/mime": "^4.4|^5.0",
|
||||||
"symfony/process": "^4.4|^5.0",
|
"symfony/process": "^4.4|^5.0",
|
||||||
|
"symfony/security-bundle": "^5.1",
|
||||||
"symfony/security-csrf": "^4.4|^5.0",
|
"symfony/security-csrf": "^4.4|^5.0",
|
||||||
"symfony/security-http": "^4.4|^5.0",
|
"symfony/security-http": "^4.4|^5.0",
|
||||||
"symfony/serializer": "^4.4|^5.0",
|
"symfony/serializer": "^4.4|^5.0",
|
||||||
|
|
Reference in New Issue