Merge branch '2.3' into 2.6

* 2.3:
  Fix typo
  Check instance of FormBuilderInterface instead of FormBuilder
  [Security] TokenBasedRememberMeServices test to show why encoding username is required
  [Security] AbstractRememberMeServices::encodeCookie() validates cookie parts
  [console][formater] allow format toString object.
  [HttpFoundation] Fix baseUrl when script filename is contained in pathInfo
  Avoid redirection to XHR URIs
  [HttpFoundation] IpUtils::checkIp4() should allow  networks
  Fix HTML escaping of to-source links
  [FrameworkBundle] Removed unnecessary parameter in TemplateController
  [DomCrawler] Throw an exception if a form field path is incomplete.
  [Console] Delete duplicate test in CommandTest
  [TwigBundle] Refresh twig paths when resources change.
  WebProfiler break words
  fixed typo
  Update README.md
  [HttpKernel] Handle an array vary header in the http cache store
  [Security][Translation] fixes #14584
  [Framework] added test for Router commands.
  Handled bearer authorization header in REDIRECT_ form

Conflicts:
	src/Symfony/Component/Debug/ExceptionHandler.php
This commit is contained in:
Fabien Potencier 2015-05-22 16:53:08 +02:00
commit dd744c9f53
31 changed files with 385 additions and 45 deletions

View File

@ -33,8 +33,8 @@ work for you:
Installation Installation
------------ ------------
The best way to install Symfony is to download the Symfony Standard Edition The best way to install Symfony is to use the [official Symfony Installer][7].
available at <https://symfony.com/download>. It allows you to start a new project based on the version you want.
Documentation Documentation
------------- -------------
@ -64,3 +64,4 @@ Information on how to run the Symfony test suite can be found in the
[4]: https://symfony.com/doc/current/contributing/code/patches.html#check-list [4]: https://symfony.com/doc/current/contributing/code/patches.html#check-list
[5]: https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request [5]: https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request
[6]: https://symfony.com/doc/master/contributing/code/tests.html [6]: https://symfony.com/doc/master/contributing/code/tests.html
[7]: https://symfony.com/doc/current/book/installation.html#installing-the-symfony-installer

View File

@ -49,7 +49,7 @@ class CodeExtension extends \Twig_Extension
new \Twig_SimpleFilter('file_excerpt', array($this, 'fileExcerpt'), array('is_safe' => array('html'))), new \Twig_SimpleFilter('file_excerpt', array($this, 'fileExcerpt'), array('is_safe' => array('html'))),
new \Twig_SimpleFilter('format_file', array($this, 'formatFile'), array('is_safe' => array('html'))), new \Twig_SimpleFilter('format_file', array($this, 'formatFile'), array('is_safe' => array('html'))),
new \Twig_SimpleFilter('format_file_from_text', array($this, 'formatFileFromText'), array('is_safe' => array('html'))), new \Twig_SimpleFilter('format_file_from_text', array($this, 'formatFileFromText'), array('is_safe' => array('html'))),
new \Twig_SimpleFilter('file_link', array($this, 'getFileLink'), array('is_safe' => array('html'))), new \Twig_SimpleFilter('file_link', array($this, 'getFileLink')),
); );
} }

View File

@ -47,7 +47,7 @@ class TemplateController extends ContainerAware
if ($private) { if ($private) {
$response->setPrivate(); $response->setPrivate();
} elseif ($private === false || (null === $private && ($maxAge || $sharedAge))) { } elseif ($private === false || (null === $private && ($maxAge || $sharedAge))) {
$response->setPublic($private); $response->setPublic();
} }
return $response; return $response;

View File

@ -52,6 +52,7 @@ build: 56
background-color: #FFFFFF; background-color: #FFFFFF;
border: 1px solid #dfdfdf; border: 1px solid #dfdfdf;
padding: 40px 50px; padding: 40px 50px;
word-break: break-all;
} }
.sf-reset h2 { .sf-reset h2 {
font-size: 16px; font-size: 16px;

View File

@ -154,24 +154,25 @@ class CodeHelper extends Helper
*/ */
public function formatFile($file, $line, $text = null) public function formatFile($file, $line, $text = null)
{ {
if (PHP_VERSION_ID >= 50400) {
$flags = ENT_QUOTES | ENT_SUBSTITUTE;
} else {
$flags = ENT_QUOTES;
}
if (null === $text) { if (null === $text) {
$file = trim($file); $file = trim($file);
$fileStr = $file; $fileStr = $file;
if (0 === strpos($fileStr, $this->rootDir)) { if (0 === strpos($fileStr, $this->rootDir)) {
$fileStr = str_replace($this->rootDir, '', str_replace('\\', '/', $fileStr)); $fileStr = str_replace($this->rootDir, '', str_replace('\\', '/', $fileStr));
$fileStr = sprintf('<abbr title="%s">kernel.root_dir</abbr>/%s', $this->rootDir, $fileStr); $fileStr = htmlspecialchars($fileStr, $flags, $this->charset);
$fileStr = sprintf('<abbr title="%s">kernel.root_dir</abbr>/%s', htmlspecialchars($this->rootDir, $flags, $this->charset), $fileStr);
} }
$text = "$fileStr at line $line"; $text = sprintf('%s at line %d', $fileStr, $line);
} }
if (false !== $link = $this->getFileLink($file, $line)) { if (false !== $link = $this->getFileLink($file, $line)) {
if (PHP_VERSION_ID >= 50400) {
$flags = ENT_QUOTES | ENT_SUBSTITUTE;
} else {
$flags = ENT_QUOTES;
}
return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, $flags, $this->charset), $text); return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, $flags, $this->charset), $text);
} }

View File

@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class RouterDebugCommandTest extends \PHPUnit_Framework_TestCase
{
public function testDebugAllRoutes()
{
$tester = $this->createCommandTester();
$ret = $tester->execute(array('name' => null));
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertContains('[router] Current routes', $tester->getDisplay());
}
public function testDebugSingleRoute()
{
$tester = $this->createCommandTester();
$ret = $tester->execute(array('name' => 'foo'));
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertContains('[router] Route "foo"', $tester->getDisplay());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testDebugInvalidRoute()
{
$this->createCommandTester()->execute(array('name' => 'test'));
}
/**
* @return CommandTester
*/
private function createCommandTester()
{
$application = new Application();
$command = new RouterDebugCommand();
$command->setContainer($this->getContainer());
$application->add($command);
return new CommandTester($application->find('router:debug'));
}
private function getContainer()
{
$routeCollection = new RouteCollection();
$routeCollection->add('foo', new Route('foo'));
$router = $this->getMock('Symfony\Component\Routing\RouterInterface');
$router
->expects($this->atLeastOnce())
->method('getRouteCollection')
->will($this->returnValue($routeCollection))
;
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$container
->expects($this->once())
->method('has')
->with('router')
->will($this->returnValue(true))
;
$container
->expects($this->atLeastOnce())
->method('get')
->with('router')
->will($this->returnValue($router))
;
return $container;
}
}

View File

@ -0,0 +1,93 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand;
use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RequestContext;
class RouterMatchCommandTest extends \PHPUnit_Framework_TestCase
{
public function testWithMatchPath()
{
$tester = $this->createCommandTester();
$ret = $tester->execute(array('path_info' => '/foo', 'foo'));
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
$this->assertContains('[router] Route "foo"', $tester->getDisplay());
}
public function testWithNotMatchPath()
{
$tester = $this->createCommandTester();
$ret = $tester->execute(array('path_info' => '/test', 'foo'));
$this->assertEquals(1, $ret, 'Returns 1 in case of failure');
$this->assertContains('None of the routes match the path "/test"', $tester->getDisplay());
}
/**
* @return CommandTester
*/
private function createCommandTester()
{
$application = new Application();
$command = new RouterMatchCommand();
$command->setContainer($this->getContainer());
$application->add($command);
$command = new RouterDebugCommand();
$command->setContainer($this->getContainer());
$application->add($command);
return new CommandTester($application->find('router:match'));
}
private function getContainer()
{
$routeCollection = new RouteCollection();
$routeCollection->add('foo', new Route('foo'));
$requestContext = new RequestContext();
$router = $this->getMock('Symfony\Component\Routing\RouterInterface');
$router
->expects($this->any())
->method('getRouteCollection')
->will($this->returnValue($routeCollection))
;
$router
->expects($this->any())
->method('getContext')
->will($this->returnValue($requestContext))
;
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$container
->expects($this->once())
->method('has')
->with('router')
->will($this->returnValue(true))
;
$container
->expects($this->atLeastOnce())
->method('get')
->with('router')
->will($this->returnValue($router))
;
return $container;
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Bundle\TwigBundle\DependencyInjection; namespace Symfony\Bundle\TwigBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Resource\DirectoryResource;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@ -66,22 +67,26 @@ class TwigExtension extends Extension
} else { } else {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
} }
$container->addResource(new DirectoryResource($path));
} }
// register bundles as Twig namespaces // register bundles as Twig namespaces
foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { foreach ($container->getParameter('kernel.bundles') as $bundle => $class) {
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views')) { if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views')) {
$this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle);
$container->addResource(new DirectoryResource($dir));
} }
$reflection = new \ReflectionClass($class); $reflection = new \ReflectionClass($class);
if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/views')) { if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/views')) {
$this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle);
$container->addResource(new DirectoryResource($dir));
} }
} }
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) { if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir)); $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir));
$container->addResource(new DirectoryResource($dir));
} }
if (!empty($config['globals'])) { if (!empty($config['globals'])) {

View File

@ -142,6 +142,7 @@ class OutputFormatter implements OutputFormatterInterface
*/ */
public function format($message) public function format($message)
{ {
$message = (string) $message;
$offset = 0; $offset = 0;
$output = ''; $output = '';
$tagRegex = '[a-z][a-z0-9_=;-]*'; $tagRegex = '[a-z][a-z0-9_=;-]*';

View File

@ -167,15 +167,6 @@ class CommandTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper');
} }
public function testGet()
{
$application = new Application();
$command = new \TestCommand();
$command->setApplication($application);
$formatterHelper = new FormatterHelper();
$this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->__get() returns the correct helper');
}
public function testMergeApplicationDefinition() public function testMergeApplicationDefinition()
{ {
$application1 = new Application(); $application1 = new Application();

View File

@ -166,6 +166,14 @@ class OutputFormatterTest extends \PHPUnit_Framework_TestCase
$this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('<error>some error</error>'.$long)); $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('<error>some error</error>'.$long));
} }
public function testFormatToStringObject()
{
$formatter = new OutputFormatter(false);
$this->assertEquals(
'some info', $formatter->format(new TableCell())
);
}
public function testNotDecoratedFormatter() public function testNotDecoratedFormatter()
{ {
$formatter = new OutputFormatter(false); $formatter = new OutputFormatter(false);
@ -255,3 +263,11 @@ EOF
)); ));
} }
} }
class TableCell
{
public function __toString()
{
return '<info>some info</info>';
}
}

View File

@ -124,13 +124,15 @@ class FormFieldRegistry
public function set($name, $value) public function set($name, $value)
{ {
$target = &$this->get($name); $target = &$this->get($name);
if (!is_array($value) || $target instanceof Field\ChoiceFormField) { if ((!is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
$target->setValue($value); $target->setValue($value);
} else { } elseif (is_array($value)) {
$fields = self::create($name, $value); $fields = self::create($name, $value);
foreach ($fields->all() as $k => $v) { foreach ($fields->all() as $k => $v) {
$this->set($k, $v); $this->set($k, $v);
} }
} else {
throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name));
} }
} }

View File

@ -807,6 +807,31 @@ class FormTest extends \PHPUnit_Framework_TestCase
)); ));
} }
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Cannot set value on a compound field "foo[bar]".
*/
public function testFormRegistrySetValueOnCompoundField()
{
$registry = new FormFieldRegistry();
$registry->add($this->getFormFieldMock('foo[bar][baz]'));
$registry->set('foo[bar]', 'fbb');
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Unreachable field "0"
*/
public function testFormRegistrySetArrayOnNotCompoundField()
{
$registry = new FormFieldRegistry();
$registry->add($this->getFormFieldMock('bar'));
$registry->set('bar', array('baz'));
}
public function testDifferentFieldTypesWithSameName() public function testDifferentFieldTypesWithSameName()
{ {
$dom = new \DOMDocument(); $dom = new \DOMDocument();

View File

@ -62,7 +62,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
} }
if ($child instanceof self) { if ($child instanceof FormBuilderInterface) {
$this->children[$child->getName()] = $child; $this->children[$child->getName()] = $child;
// In case an unresolved child with the same name exists // In case an unresolved child with the same name exists
@ -72,7 +72,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
} }
if (!is_string($child) && !is_int($child)) { if (!is_string($child) && !is_int($child)) {
throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormBuilder'); throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormBuilderInterface');
} }
if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) { if (null !== $type && !is_string($type) && !$type instanceof FormTypeInterface) {

View File

@ -11,7 +11,9 @@
namespace Symfony\Component\Form\Tests; namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\ButtonBuilder;
use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\SubmitButtonBuilder;
class FormBuilderTest extends \PHPUnit_Framework_TestCase class FormBuilderTest extends \PHPUnit_Framework_TestCase
{ {
@ -154,6 +156,12 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase
$this->builder->create('foo'); $this->builder->create('foo');
} }
public function testAddButton()
{
$this->builder->add(new ButtonBuilder('reset'));
$this->builder->add(new SubmitButtonBuilder('submit'));
}
public function testGetUnknown() public function testGetUnknown()
{ {
$this->setExpectedException('Symfony\Component\Form\Exception\InvalidArgumentException', 'The child with the name "foo" does not exist.'); $this->setExpectedException('Symfony\Component\Form\Exception\InvalidArgumentException', 'The child with the name "foo" does not exist.');

View File

@ -62,6 +62,10 @@ class IpUtils
public static function checkIp4($requestIp, $ip) public static function checkIp4($requestIp, $ip)
{ {
if (false !== strpos($ip, '/')) { if (false !== strpos($ip, '/')) {
if ('0.0.0.0/0' === $ip) {
return true;
}
list($address, $netmask) = explode('/', $ip, 2); list($address, $netmask) = explode('/', $ip, 2);
if ($netmask < 1 || $netmask > 32) { if ($netmask < 1 || $netmask > 32) {

View File

@ -1760,7 +1760,7 @@ class Request
return $prefix; return $prefix;
} }
if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl).'/')) { if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/').'/')) {
// directory portion of $baseUrl matches // directory portion of $baseUrl matches
return rtrim($prefix, '/'); return rtrim($prefix, '/');
} }

View File

@ -75,6 +75,13 @@ class ServerBag extends ParameterBag
// In some circumstances PHP_AUTH_DIGEST needs to be set // In some circumstances PHP_AUTH_DIGEST needs to be set
$headers['PHP_AUTH_DIGEST'] = $authorizationHeader; $headers['PHP_AUTH_DIGEST'] = $authorizationHeader;
$this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader; $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader;
} elseif (0 === stripos($authorizationHeader, 'bearer ')) {
/*
* XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
* I'll just set $headers['AUTHORIZATION'] here.
* http://php.net/manual/en/reserved.variables.server.php
*/
$headers['AUTHORIZATION'] = $authorizationHeader;
} }
} }
} }

View File

@ -34,6 +34,9 @@ class IpUtilsTest extends \PHPUnit_Framework_TestCase
array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')), array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')),
array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')), array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')),
array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')), array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')),
array(true, '1.2.3.4', '0.0.0.0/0'),
array(false, '1.2.3.4', '256.256.256/0'),
array(false, '1.2.3.4', '192.168.1.0/0'),
); );
} }

View File

@ -1345,6 +1345,26 @@ class RequestTest extends \PHPUnit_Framework_TestCase
public function getBaseUrlData() public function getBaseUrlData()
{ {
return array( return array(
array(
'/fruit/strawberry/1234index.php/blah',
array(
'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/fruit/index.php',
'SCRIPT_NAME' => '/fruit/index.php',
'PHP_SELF' => '/fruit/index.php',
),
'/fruit',
'/strawberry/1234index.php/blah',
),
array(
'/fruit/strawberry/1234index.php/blah',
array(
'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/index.php',
'SCRIPT_NAME' => '/index.php',
'PHP_SELF' => '/index.php',
),
'',
'/fruit/strawberry/1234index.php/blah',
),
array( array(
'/foo%20bar/', '/foo%20bar/',
array( array(

View File

@ -141,4 +141,14 @@ class ServerBagTest extends \PHPUnit_Framework_TestCase
'AUTHORIZATION' => $headerContent, 'AUTHORIZATION' => $headerContent,
), $bag->getHeaders()); ), $bag->getHeaders());
} }
public function testOAuthBearerAuthWithRedirect()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent));
$this->assertEquals(array(
'AUTHORIZATION' => $headerContent,
), $bag->getHeaders());
}
} }

View File

@ -127,7 +127,7 @@ class Store implements StoreInterface
// find a cached entry that matches the request. // find a cached entry that matches the request.
$match = null; $match = null;
foreach ($entries as $entry) { foreach ($entries as $entry) {
if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? $entry[1]['vary'][0] : '', $request->headers->all(), $entry[0])) { if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? implode(', ', $entry[1]['vary']) : '', $request->headers->all(), $entry[0])) {
$match = $entry; $match = $entry;
break; break;

View File

@ -168,6 +168,16 @@ class StoreTest extends \PHPUnit_Framework_TestCase
$this->assertNull($this->store->lookup($req2)); $this->assertNull($this->store->lookup($req2));
} }
public function testDoesNotReturnEntriesThatSlightlyVaryWithLookup()
{
$req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
$req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bam'));
$res = new Response('test', 200, array('Vary' => array('Foo', 'Bar')));
$this->store->write($req1, $res);
$this->assertNull($this->store->lookup($req2));
}
public function testStoresMultipleResponsesForEachVaryCombination() public function testStoresMultipleResponsesForEachVaryCombination()
{ {
$req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));

View File

@ -119,7 +119,7 @@ class IntlDateFormatter
/** /**
* @var bool * @var bool
*/ */
private $unitializedTimeZoneId = false; private $uninitializedTimeZoneId = false;
/** /**
* @var string * @var string
@ -371,7 +371,7 @@ class IntlDateFormatter
*/ */
public function getTimeZoneId() public function getTimeZoneId()
{ {
if (!$this->unitializedTimeZoneId) { if (!$this->uninitializedTimeZoneId) {
return $this->timeZoneId; return $this->timeZoneId;
} }
@ -551,7 +551,7 @@ class IntlDateFormatter
$timeZoneId = getenv('TZ') ?: 'UTC'; $timeZoneId = getenv('TZ') ?: 'UTC';
} }
$this->unitializedTimeZoneId = true; $this->uninitializedTimeZoneId = true;
} }
// Backup original passed time zone // Backup original passed time zone

View File

@ -201,7 +201,7 @@ class ExceptionListener
protected function setTargetPath(Request $request) protected function setTargetPath(Request $request)
{ {
// session isn't required when using HTTP basic authentication mechanism for example // session isn't required when using HTTP basic authentication mechanism for example
if ($request->hasSession() && $request->isMethodSafe()) { if ($request->hasSession() && $request->isMethodSafe() && !$request->isXmlHttpRequest()) {
$request->getSession()->set('_security.'.$this->providerKey.'.target_path', $request->getUri()); $request->getSession()->set('_security.'.$this->providerKey.'.target_path', $request->getUri());
} }
} }

View File

@ -268,9 +268,17 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
* @param array $cookieParts * @param array $cookieParts
* *
* @return string * @return string
*
* @throws \InvalidArgumentException When $cookieParts contain the cookie delimiter. Extending class should either remove or escape it.
*/ */
protected function encodeCookie(array $cookieParts) protected function encodeCookie(array $cookieParts)
{ {
foreach ($cookieParts as $cookiePart) {
if (false !== strpos($cookiePart, self::COOKIE_DELIMITER)) {
throw new \InvalidArgumentException(sprintf('$cookieParts should not contain the cookie delimiter "%s"', self::COOKIE_DELIMITER));
}
}
return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts)); return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts));
} }

View File

@ -95,12 +95,12 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
* @param int $expires The Unix timestamp when the cookie expires * @param int $expires The Unix timestamp when the cookie expires
* @param string $password The encoded password * @param string $password The encoded password
* *
* @throws \RuntimeException if username contains invalid chars
*
* @return string * @return string
*/ */
protected function generateCookieValue($class, $username, $expires, $password) protected function generateCookieValue($class, $username, $expires, $password)
{ {
// $username is encoded because it might contain COOKIE_DELIMITER,
// we assume other values don't
return $this->encodeCookie(array( return $this->encodeCookie(array(
$class, $class,
base64_encode($username), base64_encode($username),
@ -117,8 +117,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
* @param int $expires The Unix timestamp when the cookie expires * @param int $expires The Unix timestamp when the cookie expires
* @param string $password The encoded password * @param string $password The encoded password
* *
* @throws \RuntimeException when the private key is empty
*
* @return string * @return string
*/ */
protected function generateCookieHash($class, $username, $expires, $password) protected function generateCookieHash($class, $username, $expires, $password)

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Http\Tests\RememberMe;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices;
class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
{ {
@ -236,6 +237,30 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
); );
} }
public function testEncodeCookieAndDecodeCookieAreInvertible()
{
$cookieParts = array('aa', 'bb', 'cc');
$service = $this->getService();
$encoded = $this->callProtected($service, 'encodeCookie', array($cookieParts));
$this->assertInternalType('string', $encoded);
$decoded = $this->callProtected($service, 'decodeCookie', array($encoded));
$this->assertSame($cookieParts, $decoded);
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage cookie delimiter
*/
public function testThereShouldBeNoCookieDelimiterInCookieParts()
{
$cookieParts = array('aa', 'b'.AbstractRememberMeServices::COOKIE_DELIMITER.'b', 'cc');
$service = $this->getService();
$this->callProtected($service, 'encodeCookie', array($cookieParts));
}
protected function getService($userProvider = null, $options = array(), $logger = null) protected function getService($userProvider = null, $options = array(), $logger = null)
{ {
if (null === $userProvider) { if (null === $userProvider) {
@ -258,4 +283,13 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
return $provider; return $provider;
} }
private function callProtected($object, $method, array $args)
{
$reflection = new \ReflectionClass(get_class($object));
$reflectionMethod = $reflection->getMethod($method);
$reflectionMethod->setAccessible(true);
return $reflectionMethod->invokeArgs($object, $args);
}
} }

View File

@ -105,7 +105,12 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared()); $this->assertTrue($request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME)->isCleared());
} }
public function testAutoLogin() /**
* @dataProvider provideUsernamesForAutoLogin
*
* @param string $username
*/
public function testAutoLogin($username)
{ {
$user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
$user $user
@ -123,13 +128,13 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$userProvider $userProvider
->expects($this->once()) ->expects($this->once())
->method('loadUserByUsername') ->method('loadUserByUsername')
->with($this->equalTo('foouser')) ->with($this->equalTo($username))
->will($this->returnValue($user)) ->will($this->returnValue($user))
; ;
$service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600)); $service = $this->getService($userProvider, array('name' => 'foo', 'always_remember_me' => true, 'lifetime' => 3600));
$request = new Request(); $request = new Request();
$request->cookies->set('foo', $this->getCookie('fooclass', 'foouser', time() + 3600, 'foopass')); $request->cookies->set('foo', $this->getCookie('fooclass', $username, time() + 3600, 'foopass'));
$returnedToken = $service->autoLogin($request); $returnedToken = $service->autoLogin($request);
@ -138,6 +143,14 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('fookey', $returnedToken->getKey()); $this->assertEquals('fookey', $returnedToken->getKey());
} }
public function provideUsernamesForAutoLogin()
{
return array(
array('foouser', 'Simple username'),
array('foo'.TokenBasedRememberMeServices::COOKIE_DELIMITER.'user', 'Username might contain the delimiter'),
);
}
public function testLogout() public function testLogout()
{ {
$service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null)); $service = $this->getService(null, array('name' => 'foo', 'path' => null, 'domain' => null));

View File

@ -8,7 +8,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2"> <trans-unit id="2">
<source>Authentication credentials could not be found.</source> <source>Authentication credentials could not be found.</source>
<target>Les droits d'authentification n'ont pas pu être trouvés.</target> <target>Les identifiants d'authentification n'ont pas pu être trouvés.</target>
</trans-unit> </trans-unit>
<trans-unit id="3"> <trans-unit id="3">
<source>Authentication request could not be processed due to a system problem.</source> <source>Authentication request could not be processed due to a system problem.</source>
@ -16,7 +16,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4"> <trans-unit id="4">
<source>Invalid credentials.</source> <source>Invalid credentials.</source>
<target>Droits invalides.</target> <target>Identifiants invalides.</target>
</trans-unit> </trans-unit>
<trans-unit id="5"> <trans-unit id="5">
<source>Cookie has already been used by someone else.</source> <source>Cookie has already been used by someone else.</source>
@ -24,7 +24,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6"> <trans-unit id="6">
<source>Not privileged to request the resource.</source> <source>Not privileged to request the resource.</source>
<target>Pas de privilèges pour accéder à la ressource.</target> <target>Privilèges insuffisants pour accéder à la ressource.</target>
</trans-unit> </trans-unit>
<trans-unit id="7"> <trans-unit id="7">
<source>Invalid CSRF token.</source> <source>Invalid CSRF token.</source>
@ -40,7 +40,7 @@
</trans-unit> </trans-unit>
<trans-unit id="10"> <trans-unit id="10">
<source>No session available, it either timed out or cookies are not enabled.</source> <source>No session available, it either timed out or cookies are not enabled.</source>
<target>Pas de session disponible, celle-ci a expiré ou les cookies ne sont pas activés.</target> <target>Aucune session disponible, celle-ci a expiré ou les cookies ne sont pas activés.</target>
</trans-unit> </trans-unit>
<trans-unit id="11"> <trans-unit id="11">
<source>No token could be found.</source> <source>No token could be found.</source>
@ -48,7 +48,7 @@
</trans-unit> </trans-unit>
<trans-unit id="12"> <trans-unit id="12">
<source>Username could not be found.</source> <source>Username could not be found.</source>
<target>Le nom d'utilisateur ne peut pas être trouvé.</target> <target>Le nom d'utilisateur n'a pas pu être trouvé.</target>
</trans-unit> </trans-unit>
<trans-unit id="13"> <trans-unit id="13">
<source>Account has expired.</source> <source>Account has expired.</source>
@ -56,7 +56,7 @@
</trans-unit> </trans-unit>
<trans-unit id="14"> <trans-unit id="14">
<source>Credentials have expired.</source> <source>Credentials have expired.</source>
<target>Les droits ont expirés.</target> <target>Les identifiants ont expiré.</target>
</trans-unit> </trans-unit>
<trans-unit id="15"> <trans-unit id="15">
<source>Account is disabled.</source> <source>Account is disabled.</source>

View File

@ -1,7 +1,7 @@
Serializer Component Serializer Component
==================== ====================
With the Serializer component its possible to handle serializing data structures, With the Serializer component it's possible to handle serializing data structures,
including object graphs, into array structures or other formats like XML and JSON. including object graphs, into array structures or other formats like XML and JSON.
It can also handle deserializing XML and JSON back to object graphs. It can also handle deserializing XML and JSON back to object graphs.