Skip to content

Commit 2f8e1b8

Browse files
Merge branch '2.8' into 3.3
* 2.8: [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 663f3f0 + 899bf99 commit 2f8e1b8

File tree

10 files changed

+120
-26
lines changed

10 files changed

+120
-26
lines changed

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ install:
2727
- 7z x php_memcache-3.0.8-5.5-nts-vc11-x86.zip -y >nul
2828
- cd ..
2929
- copy /Y php.ini-development php.ini-min
30+
- echo memory_limit=-1 >> php.ini-min
3031
- echo serialize_precision=14 >> php.ini-min
3132
- echo max_execution_time=1200 >> php.ini-min
3233
- 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
@@ -531,9 +531,21 @@ public function __toString()
531531
return trigger_error($e, E_USER_ERROR);
532532
}
533533

534+
$cookieHeader = '';
535+
$cookies = array();
536+
537+
foreach ($this->cookies as $k => $v) {
538+
$cookies[] = $k.'='.$v;
539+
}
540+
541+
if (!empty($cookies)) {
542+
$cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
543+
}
544+
534545
return
535546
sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
536-
$this->headers."\r\n".
547+
$this->headers.
548+
$cookieHeader."\r\n".
537549
$content;
538550
}
539551

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

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

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

1527-
$this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $request->__toString());
1528+
$asString = (string) $request;
1529+
1530+
$this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $asString);
1531+
$this->assertContains('Cookie: Foo=Bar', $asString);
1532+
1533+
$request->cookies->set('Another', 'Cookie');
1534+
1535+
$asString = (string) $request;
1536+
1537+
$this->assertContains('Cookie: Foo=Bar; Another=Cookie', $asString);
15281538
}
15291539

15301540
public function testIsMethod()

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

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

114114
if (T_CLASS === $token[0]) {
115-
// Skip usage of ::class constant
116-
$isClassConstant = false;
115+
// Skip usage of ::class constant and anonymous classes
116+
$skipClassToken = false;
117117
for ($j = $i - 1; $j > 0; --$j) {
118118
if (!isset($tokens[$j][1])) {
119119
break;
120120
}
121121

122-
if (T_DOUBLE_COLON === $tokens[$j][0]) {
123-
$isClassConstant = true;
122+
if (T_DOUBLE_COLON === $tokens[$j][0] || T_NEW === $tokens[$j][0]) {
123+
$skipClassToken = true;
124124
break;
125125
} elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) {
126126
break;
127127
}
128128
}
129129

130-
if (!$isClassConstant) {
130+
if (!$skipClassToken) {
131131
$class = true;
132132
}
133133
}
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
@@ -67,6 +67,17 @@ public function testLoadVariadic()
6767
$this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php');
6868
}
6969

70+
/**
71+
* @requires PHP 7.0
72+
*/
73+
public function testLoadAnonymousClass()
74+
{
75+
$this->reader->expects($this->never())->method('getClassAnnotation');
76+
$this->reader->expects($this->never())->method('getMethodAnnotations');
77+
78+
$this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php');
79+
}
80+
7081
public function testSupports()
7182
{
7283
$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
@@ -43,8 +43,6 @@ class ContextListener implements ListenerInterface
4343
private $registered;
4444
private $trustResolver;
4545

46-
private static $unserializeExceptionCode = 0x37313bc;
47-
4846
public function __construct(TokenStorageInterface $tokenStorage, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null)
4947
{
5048
if (empty($contextKey)) {
@@ -185,7 +183,7 @@ private function safelyUnserialize($serializedToken)
185183
$prevUnserializeHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
186184
$prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$prevErrorHandler) {
187185
if (__FILE__ === $file) {
188-
throw new \UnexpectedValueException($msg, self::$unserializeExceptionCode);
186+
throw new \UnexpectedValueException($msg, 0x37313bc);
189187
}
190188

191189
return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
@@ -199,7 +197,7 @@ private function safelyUnserialize($serializedToken)
199197
restore_error_handler();
200198
ini_set('unserialize_callback_func', $prevUnserializeHandler);
201199
if ($e) {
202-
if (!$e instanceof \UnexpectedValueException || self::$unserializeExceptionCode !== $e->getCode()) {
200+
if (!$e instanceof \UnexpectedValueException || 0x37313bc !== $e->getCode()) {
203201
throw $e;
204202
}
205203
if ($this->logger) {
@@ -215,6 +213,6 @@ private function safelyUnserialize($serializedToken)
215213
*/
216214
public static function handleUnserializeCallback($class)
217215
{
218-
throw new \UnexpectedValueException('Class not found: '.$class, self::$unserializeExceptionCode);
216+
throw new \UnexpectedValueException('Class not found: '.$class, 0x37313bc);
219217
}
220218
}

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

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

1414
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1617
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
1718
use Symfony\Component\Security\Csrf\CsrfToken;
1819
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
@@ -98,15 +99,17 @@ protected function attemptAuthentication(Request $request)
9899
}
99100
}
100101

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

109-
if (strlen($username) > Security::MAX_USERNAME_LENGTH) {
110+
$username = trim($username);
111+
112+
if (\strlen($username) > Security::MAX_USERNAME_LENGTH) {
110113
throw new BadCredentialsException('Invalid username.');
111114
}
112115

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

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

1414
use Symfony\Component\HttpFoundation\Request;
1515
use Psr\Log\LoggerInterface;
16+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1617
use Symfony\Component\Security\Csrf\CsrfToken;
1718
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
1819
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
@@ -76,14 +77,16 @@ protected function attemptAuthentication(Request $request)
7677
}
7778
}
7879

79-
if ($this->options['post_only']) {
80-
$username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']));
81-
$password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']);
82-
} else {
83-
$username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']));
84-
$password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
80+
$requestBag = $this->options['post_only'] ? $request->request : $request;
81+
$username = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['username_parameter']);
82+
$password = ParameterBagUtils::getParameterBagValue($requestBag, $this->options['password_parameter']);
83+
84+
if (!\is_string($username) || (\is_object($username) && !\method_exists($username, '__toString'))) {
85+
throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($username)));
8586
}
8687

88+
$username = trim($username);
89+
8790
if (strlen($username) > Security::MAX_USERNAME_LENGTH) {
8891
throw new BadCredentialsException('Invalid username.');
8992
}

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

Lines changed: 33 additions & 1 deletion
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\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener;
17+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
18+
use Symfony\Component\HttpKernel\HttpKernelInterface;
19+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
1820
use Symfony\Component\Security\Core\Security;
21+
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
22+
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
23+
use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener;
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)