-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[DX][Testing] Added a loginUser() method to test protected resources #35997
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,8 @@ | |
|
|
||
| namespace Symfony\Bundle\FrameworkBundle; | ||
|
|
||
| use Symfony\Bundle\FrameworkBundle\Test\TestBrowserToken; | ||
| use Symfony\Component\BrowserKit\Cookie; | ||
| use Symfony\Component\BrowserKit\CookieJar; | ||
| use Symfony\Component\BrowserKit\History; | ||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
|
@@ -19,6 +21,7 @@ | |
| use Symfony\Component\HttpKernel\HttpKernelBrowser; | ||
| use Symfony\Component\HttpKernel\KernelInterface; | ||
| 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. | ||
|
|
@@ -104,6 +107,31 @@ public function enableReboot() | |
| $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))); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if I need to use the |
||
| } | ||
|
|
||
| $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} | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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().'!'); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no assertion on the roles, I'd suggest to add some, to make sure roles are passed to the user and token, WDYT? |
||
| } | ||
|
|
||
| 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()); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about adding some tests to check for users that are not in the provider?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It has to be in the user provider (as Symfony reloads the user upon every requests and if the user changed (i.e. is no longer available), the user is logged out). Do you mean adding a test that the request is unsuccesfull? |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(), | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing the typehint allowed me to have a bit nicer error message when Security is not installed.