Skip to content

Commit 899bf99

Browse files
Merge branch '2.7' into 2.8
* 2.7: [appveyor] set memory_limit=-1 [Router] Skip anonymous classes when loading annotated routes Fixed Request::__toString ignoring cookies [Security] Fix fatal error on non string username
2 parents a920061 + 95c6193 commit 899bf99

File tree

10 files changed

+119
-25
lines changed

10 files changed

+119
-25
lines changed

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ install:
3131
- 7z x php_memcache-3.0.8-5.3-nts-vc9-x86.zip -y >nul
3232
- cd ..
3333
- copy /Y php.ini-development php.ini-min
34+
- echo memory_limit=-1 >> php.ini-min
3435
- echo serialize_precision=14 >> php.ini-min
3536
- echo max_execution_time=1200 >> php.ini-min
3637
- echo date.timezone="America/Los_Angeles" >> php.ini-min

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,9 +505,21 @@ public function __toString()
505505
return trigger_error($e, E_USER_ERROR);
506506
}
507507

508+
$cookieHeader = '';
509+
$cookies = array();
510+
511+
foreach ($this->cookies as $k => $v) {
512+
$cookies[] = $k.'='.$v;
513+
}
514+
515+
if (!empty($cookies)) {
516+
$cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
517+
}
518+
508519
return
509520
sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
510-
$this->headers."\r\n".
521+
$this->headers.
522+
$cookieHeader."\r\n".
511523
$content;
512524
}
513525

src/Symfony/Component/HttpFoundation/Tests/RequestTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,18 @@ public function testToString()
14541454
$request = new Request();
14551455

14561456
$request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1457+
$request->cookies->set('Foo', 'Bar');
14571458

1458-
$this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $request->__toString());
1459+
$asString = (string) $request;
1460+
1461+
$this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $asString);
1462+
$this->assertContains('Cookie: Foo=Bar', $asString);
1463+
1464+
$request->cookies->set('Another', 'Cookie');
1465+
1466+
$asString = (string) $request;
1467+
1468+
$this->assertContains('Cookie: Foo=Bar; Another=Cookie', $asString);
14591469
}
14601470

14611471
public function testIsMethod()

src/Symfony/Component/Routing/Loader/AnnotationFileLoader.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,22 +107,22 @@ protected function findClass($file)
107107
}
108108

109109
if (T_CLASS === $token[0]) {
110-
// Skip usage of ::class constant
111-
$isClassConstant = false;
110+
// Skip usage of ::class constant and anonymous classes
111+
$skipClassToken = false;
112112
for ($j = $i - 1; $j > 0; --$j) {
113113
if (!isset($tokens[$j][1])) {
114114
break;
115115
}
116116

117-
if (T_DOUBLE_COLON === $tokens[$j][0]) {
118-
$isClassConstant = true;
117+
if (T_DOUBLE_COLON === $tokens[$j][0] || T_NEW === $tokens[$j][0]) {
118+
$skipClassToken = true;
119119
break;
120120
} elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) {
121121
break;
122122
}
123123
}
124124

125-
if (!$isClassConstant) {
125+
if (!$skipClassToken) {
126126
$class = true;
127127
}
128128
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses;
13+
14+
trait AnonymousClassInTrait
15+
{
16+
public function test()
17+
{
18+
return new class() {
19+
public function foo()
20+
{
21+
}
22+
};
23+
}
24+
}

src/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ public function testLoadVariadic()
5858
$this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php');
5959
}
6060

61+
/**
62+
* @requires PHP 7.0
63+
*/
64+
public function testLoadAnonymousClass()
65+
{
66+
$this->reader->expects($this->never())->method('getClassAnnotation');
67+
$this->reader->expects($this->never())->method('getMethodAnnotations');
68+
69+
$this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php');
70+
}
71+
6172
public function testSupports()
6273
{
6374
$fixture = __DIR__.'/../Fixtures/annotated.php';

src/Symfony/Component/Security/Http/Firewall/ContextListener.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ class ContextListener implements ListenerInterface
3939
private $dispatcher;
4040
private $registered;
4141

42-
private static $unserializeExceptionCode = 0x37313bc;
43-
4442
public function __construct(TokenStorageInterface $tokenStorage, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
4543
{
4644
if (empty($contextKey)) {
@@ -180,7 +178,7 @@ private function safelyUnserialize($serializedToken)
180178
$prevUnserializeHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
181179
$prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$prevErrorHandler) {
182180
if (__FILE__ === $file) {
183-
throw new \UnexpectedValueException($msg, self::$unserializeExceptionCode);
181+
throw new \UnexpectedValueException($msg, 0x37313bc);
184182
}
185183

186184
return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
@@ -194,7 +192,7 @@ private function safelyUnserialize($serializedToken)
194192
restore_error_handler();
195193
ini_set('unserialize_callback_func', $prevUnserializeHandler);
196194
if ($e) {
197-
if (!$e instanceof \UnexpectedValueException || self::$unserializeExceptionCode !== $e->getCode()) {
195+
if (!$e instanceof \UnexpectedValueException || 0x37313bc !== $e->getCode()) {
198196
throw $e;
199197
}
200198
if ($this->logger) {
@@ -210,6 +208,6 @@ private function safelyUnserialize($serializedToken)
210208
*/
211209
public static function handleUnserializeCallback($class)
212210
{
213-
throw new \UnexpectedValueException('Class not found: '.$class, self::$unserializeExceptionCode);
211+
throw new \UnexpectedValueException('Class not found: '.$class, 0x37313bc);
214212
}
215213
}

src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderAdapter;
1616
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
1717
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1819
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
1920
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
2021
use Symfony\Component\Security\Csrf\CsrfToken;
@@ -118,15 +119,17 @@ protected function attemptAuthentication(Request $request)
118119
}
119120
}
120121

121-
if ($this->options['post_only']) {
122-
$username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']));
123-
$password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']);
124-
} else {
125-
$username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']));
126-
$password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
122+
$requestBag = $this->options['post_only'] ? $request->request : $request;
123+
$username = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['username_parameter']);
124+
$password = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['password_parameter']);
125+
126+
if (!\is_string($username) || (\is_object($username) && !\method_exists($username, '__toString'))) {
127+
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($username)));
127128
}
128129

129-
if (strlen($username) > Security::MAX_USERNAME_LENGTH) {
130+
$username = trim($username);
131+
132+
if (\strlen($username) > Security::MAX_USERNAME_LENGTH) {
130133
throw new BadCredentialsException('Invalid username.');
131134
}
132135

src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
1616
use Symfony\Component\HttpFoundation\Request;
1717
use Psr\Log\LoggerInterface;
18+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1819
use Symfony\Component\Security\Csrf\CsrfToken;
1920
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
2021
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
@@ -95,14 +96,16 @@ protected function attemptAuthentication(Request $request)
9596
}
9697
}
9798

98-
if ($this->options['post_only']) {
99-
$username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']));
100-
$password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']);
101-
} else {
102-
$username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']));
103-
$password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
99+
$requestBag = $this->options['post_only'] ? $request->request : $request;
100+
$username = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['username_parameter']);
101+
$password = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['password_parameter']);
102+
103+
if (!\is_string($username) || (\is_object($username) && !\method_exists($username, '__toString'))) {
104+
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($username)));
104105
}
105106

107+
$username = trim($username);
108+
106109
if (strlen($username) > Security::MAX_USERNAME_LENGTH) {
107110
throw new BadCredentialsException('Invalid username.');
108111
}

src/Symfony/Component/Security/Http/Tests/Firewall/UsernamePasswordFormAuthenticationListenerTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
18+
use Symfony\Component\HttpKernel\HttpKernelInterface;
19+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
20+
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
21+
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
1722
use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener;
1823
use Symfony\Component\Security\Core\SecurityContextInterface;
24+
use Symfony\Component\Security\Http\HttpUtils;
25+
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
1926

2027
class UsernamePasswordFormAuthenticationListenerTest extends TestCase
2128
{
@@ -69,6 +76,31 @@ public function testHandleWhenUsernameLength($username, $ok)
6976
$listener->handle($event);
7077
}
7178

79+
/**
80+
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
81+
* @expectedExceptionMessage The key "_username" must be a string, "array" given.
82+
*/
83+
public function testHandleNonStringUsername()
84+
{
85+
$request = Request::create('/login_check', 'POST', array('_username' => array()));
86+
$request->setSession($this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock());
87+
88+
$listener = new UsernamePasswordFormAuthenticationListener(
89+
new TokenStorage(),
90+
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')->getMock(),
91+
new SessionAuthenticationStrategy(SessionAuthenticationStrategy::NONE),
92+
$httpUtils = new HttpUtils(),
93+
'foo',
94+
new DefaultAuthenticationSuccessHandler($httpUtils),
95+
new DefaultAuthenticationFailureHandler($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $httpUtils),
96+
array('require_previous_session' => false)
97+
);
98+
99+
$event = new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST);
100+
101+
$listener->handle($event);
102+
}
103+
72104
public function getUsernameForLength()
73105
{
74106
return array(

0 commit comments

Comments
 (0)