8000 Merge branch '6.4' into 7.0 · hhamon/symfony@63a11f4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 63a11f4

Browse files
Merge branch '6.4' into 7.0
* 6.4: [Serializer] Fix deserializing of nested snake_case attributes using CamelCaseToSnakeCaseNameConverter [Security] FormLoginAuthenticator: fail for non-string password [FrameworkBundle] Improve deprecation for handler_id/save_path config options [Serializer] Fix deserializing object collection properties [HttpKernel] Fix checking for the runtime mode in DebugLoggerConfigurator [Serializer] Fix serialized name with groups Hide username and client ip in logs
2 parents 52e98e2 + dfb43b7 commit 63a11f4

File tree

18 files changed

+245
-19
lines changed

18 files changed

+245
-19
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ CHANGELOG
3131
* Deprecate not setting the `framework.php_errors.log` config option; it will default to `true` in 7.0
3232
* Deprecate not setting the `framework.session.cookie_secure` config option; it will default to `auto` in 7.0
3333
* Deprecate not setting the `framework.session.cookie_samesite` config option; it will default to `lax` in 7.0
34-
* Deprecate not setting the `framework.session.handler_id` config option; it will default to `session.handler.native_file` when `framework.session.save_path` is set or `null` otherwise in 7.0
35-
* Deprecate not setting the `framework.session.save_path` config option when `framework.session.handler_id` is not set; it will default to `null` in 7.0
34+
* Deprecate not setting either `framework.session.handler_id` or `save_path` config options; `handler_id` will
35+
default to null in 7.0 if `save_path` is not set and to `session.handler.native_file` otherwise
36+
* Deprecate not setting the `framework.session.handler_id` config option; it will default to null,
37+
unless `save_path` is set, which will make it default to `session.handler.native_file` in 7.0
3638
* Deprecate not setting the `framework.uid.default_uuid_version` config option; it will default to `7` in 7.0
3739
* Deprecate not setting the `framework.uid.time_based_uuid_version` config option; it will default to `7` in 7.0
3840
* Deprecate not setting the `framework.validation.email_validation_mode` config option; it will default to `html5` in 7.0

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -660,12 +660,8 @@ private function addSessionSection(ArrayNodeDefinition $rootNode): void
660660
trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.session.cookie_samesite" config option is deprecated. It will default to "lax" in 7.0.');
661661
}
662662

663-
if (!\array_key_exists('handler_id', $v['session'])) {
664-
if (!\array_key_exists('save_path', $v['session'])) {
665-
trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.session.save_path" config option when the "framework.session.handler_id" config option is not set either is deprecated. Both options will default to "null" in 7.0.');
666-
} else {
667-
trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting the "framework.session.handler_id" config option is deprecated. It will default to "session.handler.native_file" when "framework.session.save_path" is set or "null" otherwise in 7.0.');
668-
}
663+
if (!\array_key_exists('handler_id', $v['session']) && !\array_key_exists('handler_id', $v['save_path'])) {
664+
trigger_deprecation('symfony/framework-bundle', '6.4', 'Not setting either "framework.session.handler_id" or "save_path" config options is deprecated; "handler_id" will default to null in 7.0 if "save_path" is not set and to "session.handler.native_file" otherwise.');
669665
}
670666
}
671667

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
7676
$container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class)
7777
->addArgument(new Reference('limiter.'.$globalId))
7878
->addArgument(new Reference('limiter.'.$localId))
79+
->addArgument('%kernel.secret%')
7980
;
8081
}
8182

src/Symfony/Component/HttpKernel/Log/DebugLoggerConfigurator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class DebugLoggerConfigurator
2222

2323
public function __construct(DebugLoggerInterface $processor, bool $enable = null)
2424
{
25-
if ($enable ?? \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
25+
if ($enable ?? !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
2626
$this->processor = $processor;
2727
}
2828
}

src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ private function getCredentials(Request $request): array
131131

132132
$request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $credentials['username']);
133133

134+
if (!\is_string($credentials['password']) && (!\is_object($credentials['password']) || !method_exists($credentials['password'], '__toString'))) {
135+
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['password_parameter'], \gettype($credentials['password'])));
136+
}
137+
134138
return $credentials;
135139
}
136140

src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,20 @@ final class DefaultLoginRateLimiter extends AbstractRequestRateLimiter
2828
{
2929
private RateLimiterFactory $globalFactory;
3030
private RateLimiterFactory $localFactory;
31+
private string $secret;
3132

32-
public function __construct(RateLimiterFactory $globalFactory, RateLimiterFactory $localFactory)
33+
/**
34+
* @param non-empty-string $secret A secret to use for hashing the IP address and username
35+
*/
36+
public function __construct(RateLimiterFactory $globalFactory, RateLimiterFactory $localFactory, #[\SensitiveParameter] string $secret = '')
3337
{
38+
if ('' === $secret) {
39+
trigger_deprecation('symfony/security-http', '6.4', 'Calling "%s()" with an empty secret is deprecated. A non-empty secret will be mandatory in version 7.0.', __METHOD__);
40+
// throw new \Symfony\Component\Security\Core\Exception\InvalidArgumentException('A non-empty secret is required.');
41+
}
3442
$this->globalFactory = $globalFactory;
3543
$this->localFactory = $localFactory;
44+
$this->secret = $secret;
3645
}
3746

3847
protected function getLimiters(Request $request): array
@@ -41,8 +50,13 @@ protected function getLimiters(Request $request): array
4150
$username = preg_match('//u', $username) ? mb_strtolower($username, 'UTF-8') : strtolower($username);
4251

4352
return [
44-
$this->globalFactory->create($request->getClientIp()),
45-
$this->localFactory->create($username.'-'.$request->getClientIp()),
53+
$this->globalFactory->create($this->hash($request->getClientIp())),
54+
$this->localFactory->create($this->hash($username.'-'.$request->getClientIp())),
4655
];
4756
}
57+
58+
private function hash(string $data): string
59+
{
60+
return strtr(substr(base64_encode(hash_hmac('sha256', $data, $this->secret, true)), 0, 8), '/+', '._');
61+
}
4862
}

src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
2525
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
2626
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
27+
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
2728
use Symfony\Component\Security\Http\HttpUtils;
2829
use Symfony\Component\Security\Http\Tests\Authenticator\Fixtures\PasswordUpgraderProvider;
2930

@@ -126,6 +127,44 @@ public function testHandleNonStringUsernameWithToString($postOnly)
126127
$this->authenticator->authenticate($request);
127128
}
128129

130+
/**
131+
* @dataProvider postOnlyDataProvider
132+
*/
133+
public function testHandleNonStringPasswordWithArray(bool $postOnly)
134+
{
135+
$this->expectException(BadRequestHttpException::class);
136+
$this->expectExceptionMessage('The key "_password" must be a string, "array" given.');
137+
138+
$request = Request::create('/login_check', 'POST', ['_username' => 'foo', '_password' => []]);
139+
$request->setSession($this->createSession());
140+
141+
$this->setUpAuthenticator(['post_only' => $postOnly]);
142+
$this->authenticator->authenticate($request);
143+
}
144+
145+
/**
146+
* @dataProvider postOnlyDataProvider
147+
*/
148+
public function testHandleNonStringPasswordWithToString(bool $postOnly)
149+
{
150+
$passwordObject = new class() {
151+
public function __toString()
152+
{
153+
return 's$cr$t';
154+
}
155+
};
156+
157+
$request = Request::create('/login_check', 'POST', ['_username' => 'foo', '_password' => $passwordObject]);
158+
$request->setSession($this->createSession());
159+
160+
$this->setUpAuthenticator(['post_only' => $postOnly]);
161+
$passport = $this->authenticator->authenticate($request);
162+
163+
/** @var PasswordCredentials $credentialsBadge */
164+
$credentialsBadge = $passport->getBadge(PasswordCredentials::class);
165+
$this->assertSame('s$cr$t', $credentialsBadge->getPassword());
166+
}
167+
129168
public static function postOnlyDataProvider()
130169
{
131170
yield [true];

src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ protected function setUp(): void
4747
'limit' => 6,
4848
'interval' => '1 minute',
4949
], new InMemoryStorage());
50-
$limiter = new DefaultLoginRateLimiter($globalLimiter, $localLimiter);
50+
$limiter = new DefaultLoginRateLimiter($globalLimiter, $localLimiter, '$3cre7');
5151

5252
$this->listener = new LoginThrottlingListener($this->requestStack, $limiter);
5353
}

src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,14 @@ private function getCacheValueForAttributesMetadata(string $class, array $contex
127127
throw new LogicException(sprintf('Found SerializedName and SerializedPath annotations on property "%s" of class "%s".', $name, $class));
128128
}
129129

130-
$groups = $metadata->getGroups();
131-
if (!$groups && ($context[AbstractNormalizer::GROUPS] ?? [])) {
130+
$metadataGroups = $metadata->getGroups();
131+
$contextGroups = (array) ($context[AbstractNormalizer::GROUPS] ?? []);
132+
133+
if ($contextGroups && !$metadataGroups) {
132134
continue;
133135
}
134-
if ($groups && !array_intersect($groups, (array) ($context[AbstractNormalizer::GROUPS] ?? []))) {
136+
137+
if ($metadataGroups && !array_intersect($metadataGroups, $contextGroups) && !\in_array('*', $contextGroups, true)) {
135138
continue;
136139
}
137140

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,13 +302,15 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
302302
$mappedClass = $this->getMappedClass($normalizedData, $type, $context);
303303

304304
$nestedAttributes = $this->getNestedAttributes($mappedClass);
305-
$nestedData = [];
305+
$nestedData = $originalNestedData = [];
306306
$propertyAccessor = PropertyAccess::createPropertyAccessor();
307307
foreach ($nestedAttributes as $property => $serializedPath) {
308308
if (null === $value = $propertyAccessor->getValue($normalizedData, $serializedPath)) {
309309
continue;
310310
}
311-
$nestedData[$property] = $value;
311+
$convertedProperty = $this->nameConverter ? $this->nameConverter->normalize($property, $mappedClass, $format, $context) : $property;
312+
$nestedData[$convertedProperty] = $value;
313+
$originalNestedData[$property] = $value;
312314
$normalizedData = $this->removeNestedValue($serializedPath->getElements(), $normalizedData);
313315
}
314316

@@ -321,7 +323,7 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
321323
if ($this->nameConverter) {
322324
$notConverted = $attribute;
323325
$attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);
324-
if (isset($nestedData[$notConverted]) && !isset($nestedData[$attribute])) {
326+
if (isset($nestedData[$notConverted]) && !isset($originalNestedData[$attribute])) {
325327
throw new LogicException(sprintf('Duplicate values for key "%s" found. One value is set via the SerializedPath annotation: "%s", the other one is set via the SerializedName annotation: "%s".', $notConverted, implode('->', $nestedAttributes[$notConverted]->getElements()), $attribute));
326328
}
327329
}

0 commit comments

Comments
 (0)
0