Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 78eaf86

Browse filesBrowse files
committed
Add functional test & fix reviews
1 parent d7724d2 commit 78eaf86
Copy full SHA for 78eaf86

File tree

7 files changed

+130
-103
lines changed
Filter options

7 files changed

+130
-103
lines changed

‎src/Symfony/Bundle/SecurityBundle/Security/Security.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Security/Security.php
+23-18Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@
2222
/**
2323
* Helper class for commonly-needed security tasks.
2424
*
25+
* @author Ryan Weaver <ryan@symfonycasts.com>
26+
* @author Robin Chalas <robin.chalas@gmail.com>
27+
* @author Arnaud Frézet <arnaud@larriereguichet.fr>
28+
*
2529
* @final
2630
*/
2731
class Security extends LegacySecurity
2832
{
29-
public function __construct(private ContainerInterface $container, private array $authenticators = [])
33+
public function __construct(private readonly ContainerInterface $container, private readonly array $authenticators = [])
3034
{
3135
parent::__construct($container, false);
3236
}
@@ -36,14 +40,21 @@ public function getFirewallConfig(Request $request): ?FirewallConfig
3640
return $this->container->get('security.firewall.map')->getFirewallConfig($request);
3741
}
3842

43+
/**
44+
* @param UserInterface $user The user to authenticate
45+
* @param string|null $authenticatorName The authenticator name (e.g. "form_login") or service id (e.g. SomeApiKeyAuthenticator::class) - required only if multiple authenticators are configured
46+
* @param string|null $firewallName The firewall name - required only if multiple firewalls are configured
47+
*/
3948
public function login(UserInterface $user, string $authenticatorName = null, string $firewallName = null): void
4049
{
4150
$request = $this->container->get('request_stack')->getCurrentRequest();
51+
$firewallName ??= $this->getFirewallConfig($request)?->getName();
4252

43-
if (!class_exists(AuthenticatorInterface::class)) {
44-
throw new \LogicException('Security HTTP is missing. Try running "composer require symfony/security-http".');
53+
if (!$firewallName) {
54+
throw new LogicException('Unable to login as the current route is not covered by any firewall.');
4555
}
46-
$authenticator = $this->getAuthenticator($authenticatorName, $firewallName ?? $this->getFirewallName($request));
56+
57+
$authenticator = $this->getAuthenticator($authenticatorName, $firewallName);
4758

4859
$this->container->get('security.user_checker')->checkPreAuth($user);
4960
$this->container->get('security.user_authenticator')->authenticateUser($user, $authenticator, $request);
@@ -54,39 +65,33 @@ private function getAuthenticator(?string $authenticatorName, string $firewallNa
5465
if (!\array_key_exists($firewallName, $this->authenticators)) {
5566
throw new LogicException(sprintf('No authenticators found for firewall "%s".', $firewallName));
5667
}
68+
5769
/** @var ServiceProviderInterface $firewallAuthenticatorLocator */
5870
$firewallAuthenticatorLocator = $this->authenticators[$firewallName];
5971

6072
if (!$authenticatorName) {
6173
$authenticatorIds = array_keys($firewallAuthenticatorLocator->getProvidedServices());
6274

6375
if (!$authenticatorIds) {
64-
throw new LogicException('No authenticator was found for the firewall "%s".');
76+
throw new LogicException(sprintf('No authenticator was found for the firewall "%s".', $firewallName));
6577
}
66-
6778
if (1 < \count($authenticatorIds)) {
6879
throw new LogicException(sprintf('Too much authenticators were found for the current firewall "%s". You must provide an instance of "%s" to login programmatically. The available authenticators for the firewall "%s" are "%s".', $firewallName, AuthenticatorInterface::class, $firewallName, implode('" ,"', $authenticatorIds)));
6980
}
7081

7182
return $firewallAuthenticatorLocator->get($authenticatorIds[0]);
7283
}
73-
$authenticatorId = 'security.authenticator.'.$authenticatorName.'.'.$firewallName;
7484

75-
if (!$firewallAuthenticatorLocator->has($authenticatorId)) {
76-
throw new LogicException(sprintf('Unable to find an authenticator named "%s" for the firewall "%s". Try to pass a firewall name in the Security::login() method.', $authenticatorName, $firewallName));
85+
if ($firewallAuthenticatorLocator->has($authenticatorName)) {
86+
return $firewallAuthenticatorLocator->get($authenticatorName);
7787
}
7888

79-
return $firewallAuthenticatorLocator->get($authenticatorId);
80-
}
81-
82-
private function getFirewallName(Request $request): string
83-
{
84-
$firewall = $this->container->get('security.firewall.map')->getFirewallConfig($request);
89+
$authenticatorId = 'security.authenticator.'.$authenticatorName.'.'.$firewallName;
8590

86-
if (null === $firewall) {
87-
throw new LogicException('No firewall found as the current route is not covered by any firewall.');
91+
if (!$firewallAuthenticatorLocator->has($authenticatorId)) {
92+
throw new LogicException(sprintf('Unable to find an authenticator named "%s" for the firewall "%s". Available authenticators: "%s".', $authenticatorName, implode('", "', $firewallAuthenticatorLocator->getProvidedServices())));
8893
}
8994

90-
return $firewall->getName();
95+
return $firewallAuthenticatorLocator->get($authenticatorId);
9196
}
9297
}

‎src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php
+35Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
1313

1414
use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
15+
use Symfony\Bundle\SecurityBundle\Security\Security;
1516
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\SecuredPageBundle\Security\Core\User\ArrayUserProvider;
17+
use Symfony\Component\HttpFoundation\JsonResponse;
1618
use Symfony\Component\HttpFoundation\Request;
1719
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
1820
use Symfony\Component\Security\Core\User\InMemoryUser;
@@ -81,6 +83,22 @@ public function userWillBeMarkedAsChangedIfRolesHasChangedProvider()
8183
],
8284
];
8385
}
86+
87+
/**
88+
* @testWith ["json_login"]
89+
* ["Symfony\\Bundle\\SecurityBundle\\Tests\\Functional\\Bundle\\AuthenticatorBundle\\ApiAuthenticator"]
90+
*/
91+
public function testLoginWithBuiltInAuthenticator(string $authenticator)
92+
{
93+
$client = $this->createClient(['test_case' => 'SecurityHelper', 'root_config' => 'config.yml', 'debug' => true]);
94+
static::getContainer()->get(WelcomeController::class)->authenticator = $authenticator;
95+
$client->request('GET', '/welcome');
96+
$response = $client->getResponse();
97+
98+
$this->assertInstanceOf(JsonResponse::class, $response);
99+
$this->assertSame(200, $response->getStatusCode());
100+
$this->assertSame(['message' => 'Welcome @chalasr!'], json_decode($response->getContent(), true));
101+
}
84102
}
85103

86104
final class UserWithoutEquatable implements UserInterface, PasswordAuthenticatedUserInterface
@@ -189,3 +207,20 @@ public function eraseCredentials(): void
189207
{
190208
}
191209
}
210+
211+
class WelcomeController
212+
{
213+
public $authenticator = 'json_login';
214+
215+
public function __construct(private Security $security)
216+
{
217+
}
218+
219+
public function welcome()
220+
{
221+
$user = new InMemoryUser('chalasr', '', ['ROLE_USER']);
222+
$this->security->login($user, $this->authenticator);
223+
224+
return new JsonResponse(['message' => sprintf('Welcome @%s!', $this->security->getUser()->getUserIdentifier())]);
225+
}
226+
}

‎src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ services:
1111
alias: security.token_storage
1212
public: true
1313

14+
Symfony\Bundle\SecurityBundle\Tests\Functional\WelcomeController:
15+
arguments: ['@security.helper']
16+
public: true
17+
18+
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator: ~
19+
1420
security:
1521
enable_authenticator_manager: true
1622
providers:
@@ -20,3 +26,11 @@ security:
2026

2127
firewalls:
2228
default:
29+
json_login:
30+
username_path: user.login
31+
password_path: user.password
32+
custom_authenticators:
33+
- 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator'
34+
35+
access_control:
36+
- { path: ^/foo, roles: PUBLIC_ACCESS }
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
welcome:
2+
path: /welcome
3+
defaults: { _controller: Symfony\Bundle\SecurityBundle\Tests\Functional\WelcomeController::welcome }

‎src/Symfony/Bundle/SecurityBundle/Tests/Security/SecurityTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Security/SecurityTest.php
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@
1818
use Symfony\Bundle\SecurityBundle\Security\Security;
1919
use Symfony\Component\DependencyInjection\ServiceLocator;
2020
use Symfony\Component\HttpFoundation\Request;
21+
use Symfony\Component\HttpFoundation\RequestStack;
2122
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
2223
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
2324
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
2425
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
2526
use Symfony\Component\Security\Core\User\InMemoryUser;
27+
use Symfony\Component\Security\Core\User\UserCheckerInterface;
28+
use Symfony\Component\Security\Core\User\UserInterface;
29+
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
30+
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
31+
use Symfony\Contracts\Service\ServiceProviderInterface;
2632

2733
class SecurityTest extends TestCase
2834
{
@@ -111,6 +117,52 @@ public function getFirewallConfigTests()
111117
yield [$request, new FirewallConfig('main', 'acme_user_checker')];
112118
}
113119

120+
public function testAutoLogin()
121+
{
122+
$request = new Request();
123+
$authenticator = $this->createMock(AuthenticatorInterface::class);
124+
$requestStack = $this->createMock(RequestStack::class);
125+
$firewallMap = $this->createMock(FirewallMap::class);
126+
$firewall = new FirewallConfig('main', 'main');
127+
$userAuthenticator = $this->createMock(UserAuthenticatorInterface::class);
128+
$user = $this->createMock(UserInterface::class);
129+
$userChecker = $this->createMock(UserCheckerInterface::class);
130+
131+
$container = $this->createMock(ContainerInterface::class);
132+
$container
133+
->expects($this->atLeastOnce())
134+
->method('get')
135+
->willReturnMap([
136+
['request_stack', $requestStack],
137+
['security.firewall.map', $firewallMap],
138+
['security.user_authenticator', $userAuthenticator],
139+
['security.user_checker', $userChecker],
140+
])
141+
;
142+
143+
$requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request);
144+
$firewallMap->expects($this->once())->method('getFirewallConfig')->willReturn($firewall);
145+
$userAuthenticator->expects($this->once())->method('authenticateUser')->with($user, $authenticator, $request);
146+
$userChecker->expects($this->once())->method('checkPreAuth')->with($user);
147+
148+
$firewallAuthenticatorLocator = $this->createMock(ServiceProviderInterface::class);
149+
$firewallAuthenticatorLocator
150+
->expects($this->once())
151+
->method('getProvidedServices')
152+
->willReturn(['security.authenticator.custom.dev' => $authenticator])
153+
;
154+
$firewallAuthenticatorLocator
155+
->expects($this->once())
156+
->method('get')
157+
->with('security.authenticator.custom.dev')
158+
->willReturn($authenticator)
159+
;
160+
161+
$security = new Security($container, ['main' => $firewallAuthenticatorLocator]);
162+
163+
$security->login($user);
164+
}
165+
114166
private function createContainer(string $serviceId, object $serviceObject): ContainerInterface
115167
{
116168
return new ServiceLocator([$serviceId => fn () => $serviceObject]);

‎src/Symfony/Component/Security/Core/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/CHANGELOG.md
-6Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ CHANGELOG
66

77
* Deprecate the `Security` class, use `Symfony\Bundle\SecurityBundle\Security\Security` instead
88

9-
6.1
10-
---
11-
12-
* Add `Security::login()` to login programmatically
13-
14-
159
6.0
1610
---
1711

‎src/Symfony/Component/Security/Core/Tests/SecurityTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Tests/SecurityTest.php
+3-79Lines changed: 3 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,12 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Psr\Container\ContainerInterface;
16-
use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
17-
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
18-
use Symfony\Component\HttpFoundation\Request;
19-
use Symfony\Component\HttpFoundation\RequestStack;
2016
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
2117
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
2218
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
2319
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
2420
use Symfony\Component\Security\Core\Security;
2521
use Symfony\Component\Security\Core\User\InMemoryUser;
26-
use Symfony\Component\Security\Core\User\UserCheckerInterface;
27-
use Symfony\Component\Security\Core\User\UserInterface;
28-
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
29-
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
30-
use Symfony\Contracts\Service\ServiceProviderInterface;
3122

3223
/**
3324
* @group legacy
@@ -45,7 +36,7 @@ public function testGetToken()
4536

4637
$container = $this->createContainer('security.token_storage', $tokenStorage);
4738

48-
$security = new Security($container, []);
39+
$security = new Security($container);
4940
$this->assertSame($token, $security->getToken());
5041
}
5142

@@ -66,7 +57,7 @@ public function testGetUser($userInToken, $expectedUser)
6657

6758
$container = $this->createContainer('security.token_storage', $tokenStorage);
6859

69-
$security = new Security($container, []);
60+
$security = new Security($container);
7061
$this->assertSame($expectedUser, $security->getUser());
7162
}
7263

@@ -89,77 +80,10 @@ public function testIsGranted()
8980

9081
$container = $this->createContainer('security.authorization_checker', $authorizationChecker);
9182

92-
$security = new Security($container, []);
83+
$security = new Security($container);
9384
$this->assertTrue($security->isGranted('SOME_ATTRIBUTE', 'SOME_SUBJECT'));
9485
}
9586

96-
public function testAutoLogin()
97-
{
98-
$request = new Request();
99-
$authenticator = $this->createMock(AuthenticatorInterface::class);
100-
$requestStack = $this->createMock(RequestStack::class);
101-
$firewallMap = $this->createMock(FirewallMap::class);
102-
$firewall = new FirewallConfig('main', 'main');
103-
$userAuthenticator = $this->createMock(UserAuthenticatorInterface::class);
104-
$user = $this->createMock(UserInterface::class);
105-
$userChecker = $this->createMock(UserCheckerInterface::class);
106-
107-
$container = $this->createMock(ContainerInterface::class);
108-
$container
109-
->expects($this->atLeastOnce())
110-
->method('get')
111-
->willReturnMap([
112-
['request_stack', $requestStack],
113-
['security.firewall.map', $firewallMap],
114-
['security.user_authenticator', $userAuthenticator],
115-
['security.user_checker', $userChecker],
116-
])
117-
;
118-
119-
$requestStack
120-
->expects($this->once())
121-
->method('getCurrentRequest')
122-
->willReturn($request)
123-
;
124-
125-
$firewallMap
126-
->expects($this->once())
127-
->method('getFirewallConfig')
128-
->willReturn($firewall)
129-
;
130-
$userAuthenticator
131-
->expects($this->once())
132-
->method('authenticateUser')
133-
->with($user, $authenticator, $request)
134-
;
135-
$userChecker
136-
->expects($this->once())
137-
->method('checkPreAuth')
138-
->with($user)
139-
;
140-
141-
$firewallAuthenticatorLocator = $this->createMock(ServiceProviderInterface::class);
142-
$firewallAuthenticatorLocator
143-
->expects($this->once())
144-
->method('getProvidedServices')
145-
->willReturn([
146-
'security.authenticator.custom.dev' => $authenticator,
147-
])
148-
;
149-
$firewallAuthenticatorLocator
150-
->expects($this->once())
151-
->method('get')
152-
->with('security.authenticator.custom.dev')
153-
->willReturn($authenticator)
154-
;
155-
156-
$security = new Security($container, [
157-
'main' => $firewallAuthenticatorLocator,
158-
]);
159-
160-
$security->login($user);
161-
}
162-
16387
private function createContainer($serviceId, $serviceObject)
16488
{
16589
$container = $this->createMock(ContainerInterface::class);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.