Skip to content

Commit bd6219d

Browse files
committed
Merge branch '6.2' into 6.3
* 6.2: [ExpressionLanguage] Fix BC of cached SerializedParsedExpression containing GetAttrNode [HttpKernel] Fix CacheAttributeListener priority [DependencyInjection] Fix bug when tag name is a text node [VarExporter] Fix adding a key to an uninitialized array Fix missing command not matching namespace error message [SecurityBundle] Fix authenticator existence check in `Security::login()` [TwigBundle] Alias BodyRendererInterface Point `Security::*` constants to `SecurityRequestAttributes::*` ones [Translation] Fix extraction when dealing with VariadicPlaceholder parameters
2 parents c1d281d + 170c5b2 commit bd6219d

File tree

21 files changed

+344
-38
lines changed

21 files changed

+344
-38
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\HttpKernel\Attribute\Cache;
17+
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
18+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
19+
use Symfony\Component\Security\Core\User\InMemoryUser;
20+
use Symfony\Component\Security\Http\Attribute\IsGranted;
21+
22+
class CacheAttributeListenerTest extends AbstractWebTestCase
23+
{
24+
public function testAnonimousUserWithEtag()
25+
{
26+
$client = self::createClient(['test_case' => 'CacheAttributeListener']);
27+
28+
$client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]);
29+
30+
self::assertTrue($client->getResponse()->isRedirect('http://localhost/login'));
31+
}
32+
33+
public function testAnonimousUserWithoutEtag()
34+
{
35+
$client = self::createClient(['test_case' => 'CacheAttributeListener']);
36+
37+
$client->request('GET', '/');
38+
39+
self::assertTrue($client->getResponse()->isRedirect('http://localhost/login'));
40+
}
41+
42+
public function testLoggedInUserWithEtag()
43+
{
44+
$client = self::createClient(['test_case' => 'CacheAttributeListener']);
45+
46+
$client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER']));
47+
$client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]);
48+
49+
$response = $client->getResponse();
50+
51+
self::assertSame(304, $response->getStatusCode());
52+
self::assertSame('', $response->getContent());
53+
}
54+
55+
public function testLoggedInUserWithoutEtag()
56+
{
57+
$client = self::createClient(['test_case' => 'CacheAttributeListener']);
58+
59+
$client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER']));
60+
$client->request('GET', '/');
61+
62+
$response = $client->getResponse();
63+
64+
self::assertSame(200, $response->getStatusCode());
65+
self::assertSame('Hi there!', $response->getContent());
66+
}
67+
}
68+
69+
class TestEntityValueResolver implements ValueResolverInterface
70+
{
71+
public function resolve(Request $request, ArgumentMetadata $argument): iterable
72+
{
73+
return Post::class === $argument->getType() ? [new Post()] : [];
74+
}
75+
}
76+
77+
class Post
78+
{
79+
public function getId(): int
80+
{
81+
return 1;
82+
}
83+
84+
public function getEtag(): string
85+
{
86+
return '12345';
87+
}
88+
}
89+
90+
class WithAttributesController
91+
{
92+
#[IsGranted('ROLE_USER')]
93+
#[Cache(etag: 'post.getEtag()')]
94+
public function __invoke(Post $post): Response
95+
{
96+
return new Response('Hi there!');
97+
}
98+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
13+
use Symfony\Bundle\SecurityBundle\SecurityBundle;
14+
15+
return [
16+
new FrameworkBundle(),
17+
new SecurityBundle(),
18+
];
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
imports:
2+
- { resource: ../config/default.yml }
3+
4+
services:
5+
Symfony\Bundle\FrameworkBundle\Tests\Functional\TestEntityValueResolver:
6+
tags:
7+
- { name: controller.argument_value_resolver, priority: 110 }
8+
9+
Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController:
10+
public: true
11+
12+
security:
13+
providers:
14+
main:
15+
memory:
16+
users:
17+
the-username: { password: the-password, roles: [ 'ROLE_USER' ] }
18+
19+
firewalls:
20+
main:
21+
pattern: ^/
22+
form_login:
23+
login_path: /login
24+
provider: main
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
with_attributes_controller:
2+
path: /
3+
controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"symfony/error-handler": "^6.1",
2727
"symfony/event-dispatcher": "^5.4|^6.0",
2828
"symfony/http-foundation": "^6.2",
29-
"symfony/http-kernel": "^6.2",
29+
"symfony/http-kernel": "^6.2.1",
3030
"symfony/polyfill-mbstring": "~1.0",
3131
"symfony/filesystem": "^5.4|^6.0",
3232
"symfony/finder": "^5.4|^6.0",

src/Symfony/Bundle/SecurityBundle/Security.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
2525
use Symfony\Component\Security\Http\Event\LogoutEvent;
2626
use Symfony\Component\Security\Http\ParameterBagUtils;
27+
use Symfony\Component\Security\Http\SecurityRequestAttributes;
2728
use Symfony\Contracts\Service\ServiceProviderInterface;
2829

2930
/**
@@ -37,6 +38,10 @@
3738
*/
3839
class Security extends LegacySecurity
3940
{
41+
public const ACCESS_DENIED_ERROR = SecurityRequestAttributes::ACCESS_DENIED_ERROR;
42+
public const AUTHENTICATION_ERROR = SecurityRequestAttributes::AUTHENTICATION_ERROR;
43+
public const LAST_USERNAME = SecurityRequestAttributes::LAST_USERNAME;
44+
4045
public function __construct(private readonly ContainerInterface $container, private readonly array $authenticators = [])
4146
{
4247
parent::__construct($container, false);
@@ -111,7 +116,7 @@ public function logout(bool $validateCsrfToken = true): ?Response
111116

112117
private function getAuthenticator(?string $authenticatorName, string $firewallName): AuthenticatorInterface
113118
{
114-
if (!\array_key_exists($firewallName, $this->authenticators)) {
119+
if (!isset($this->authenticators[$firewallName])) {
115120
throw new LogicException(sprintf('No authenticators found for firewall "%s".', $firewallName));
116121
}
117122

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,38 @@ public function testLogin()
169169
$security->login($user);
170170
}
171171

172+
public function testLoginWithoutAuthenticatorThrows()
173+
{
174+
$request = new Request();
175+
$authenticator = $this->createMock(AuthenticatorInterface::class);
176+
$requestStack = $this->createMock(RequestStack::class);
177+
$firewallMap = $this->createMock(FirewallMap::class);
178+
$firewall = new FirewallConfig('main', 'main');
179+
$user = $this->createMock(UserInterface::class);
180+
$userChecker = $this->createMock(UserCheckerInterface::class);
181+
182+
$container = $this->createMock(ContainerInterface::class);
183+
$container
184+
->expects($this->atLeastOnce())
185+
->method('get')
186+
->willReturnMap([
187+
['request_stack', $requestStack],
188+
['security.firewall.map', $firewallMap],
189+
['security.user_checker', $userChecker],
190+
])
191+
;
192+
193+
$requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request);
194+
$firewallMap->expects($this->once())->method('getFirewallConfig')->willReturn($firewall);
195+
196+
$security = new Security($container, ['main' => null]);
197+
198+
$this->expectException(\LogicException::class);
199+
$this->expectExceptionMessage('No authenticators found for firewall "main".');
200+
201+
$security->login($user);
202+
}
203+
172204
public function testLogout()
173205
{
174206
$request = new Request();

src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Bridge\Twig\Mime\BodyRenderer;
1515
use Symfony\Component\Mailer\EventListener\MessageListener;
16+
use Symfony\Component\Mime\BodyRendererInterface;
1617

1718
return static function (ContainerConfigurator $container) {
1819
$container->services()
@@ -22,5 +23,6 @@
2223

2324
->set('twig.mime_body_renderer', BodyRenderer::class)
2425
->args([service('twig')])
26+
->alias(BodyRendererInterface::class, 'twig.mime_body_renderer')
2527
;
2628
};

src/Symfony/Component/Console/Application.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ public function doRun(InputInterface $input, OutputInterface $output)
298298

299299
return isset($event) ? $event->getExitCode() : 1;
300300
}
301+
302+
throw $e;
301303
} catch (NamespaceNotFoundException) {
302304
throw $e;
303305
}

src/Symfony/Component/Console/Tests/ApplicationTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Console\Command\HelpCommand;
1919
use Symfony\Component\Console\Command\LazyCommand;
2020
use Symfony\Component\Console\Command\SignalableCommandInterface;
21+
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
2122
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
2223
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
2324
use Symfony\Component\Console\Event\ConsoleCommandEvent;
@@ -1466,6 +1467,25 @@ public function testRunWithError()
14661467
}
14671468
}
14681469

1470+
public function testRunWithFindError()
1471+
{
1472+
$this->expectException(\Error::class);
1473+
$this->expectExceptionMessage('Find exception');
1474+
1475+
$application = new Application();
1476+
$application->setAutoExit(false);
1477+
$application->setCatchExceptions(false);
1478+
1479+
// Throws an exception when find fails
1480+
$commandLoader = $this->createMock(CommandLoaderInterface::class);
1481+
$commandLoader->method('getNames')->willThrowException(new \Error('Find exception'));
1482+
$application->setCommandLoader($commandLoader);
1483+
1484+
// The exception should not be ignored
1485+
$tester = new ApplicationTester($application);
1486+
$tester->run(['command' => 'foo']);
1487+
}
1488+
14691489
public function testRunAllowsErrorListenersToSilenceTheException()
14701490
{
14711491
$dispatcher = $this->getDispatcher();

0 commit comments

Comments
 (0)