Merge branch '3.2'

* 3.2:
  [Bridge\Doctrine] Fix change breaking doctrine-bundle test suite
  [WebProfilerBundle] Include badge status in translation tabs
  [FrameworkBundle] Cache pool clear command requires at least 1 pool
  [HttpFoundation][bugfix]  should always be initialized
  MockArraySessionStorage: updated phpdoc for $bags so that IDE autocompletion would work
  normalize paths before making them relative
  removed test that does not test anything
  fixed tests
  #21809 [SecurityBundle] bugfix: if security provider's name contains upper cases then container didn't compile
  [WebProfilerBundle] Fix for CSS attribute at Profiler Translation Page
  Set Date header in Response constructor already
  [Validator] fix URL validator to detect non supported chars according to RFC 3986
  [Security] Fixed roles serialization on token from user object
This commit is contained in:
Fabien Potencier 2017-03-26 08:50:20 -07:00
commit 811a6bf5eb
16 changed files with 133 additions and 21 deletions

View File

@ -122,7 +122,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
}
$instance['event'] = array($instance['event']);
if (isset($instance['lazy']) && $instance['lazy']) {
if ($lazy = !empty($instance['lazy'])) {
$this->container->getDefinition($id)->setPublic(true);
}
}

View File

@ -33,7 +33,7 @@ final class CachePoolClearCommand extends ContainerAwareCommand
$this
->setName('cache:pool:clear')
->setDefinition(array(
new InputArgument('pools', InputArgument::IS_ARRAY, 'A list of cache pools or cache pool clearers'),
new InputArgument('pools', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of cache pools or cache pool clearers'),
))
->setDescription('Clears cache pools')
->setHelp(<<<'EOF'

View File

@ -570,7 +570,7 @@ class SecurityExtension extends Extension
// Parses a <provider> tag and returns the id for the related user provider service
private function createUserDaoProvider($name, $provider, ContainerBuilder $container)
{
$name = $this->getUserProviderId(strtolower($name));
$name = $this->getUserProviderId($name);
// Doctrine Entity and In-memory DAO provider are managed by factories
foreach ($this->userProviderFactories as $factory) {
@ -594,7 +594,7 @@ class SecurityExtension extends Extension
if (isset($provider['chain'])) {
$providers = array();
foreach ($provider['chain']['providers'] as $providerName) {
$providers[] = new Reference($this->getUserProviderId(strtolower($providerName)));
$providers[] = new Reference($this->getUserProviderId($providerName));
}
$container
@ -609,7 +609,7 @@ class SecurityExtension extends Extension
private function getUserProviderId($name)
{
return 'security.user.provider.concrete.'.$name;
return 'security.user.provider.concrete.'.strtolower($name);
}
private function createExceptionListener($container, $config, $id, $defaultEntryPoint, $stateless)

View File

@ -8,7 +8,7 @@
{{ include('@WebProfiler/Icon/translation.svg') }}
{% set status_color = collector.countMissings ? 'red' : collector.countFallbacks ? 'yellow' %}
{% set error_count = collector.countMissings + collector.countFallbacks %}
<span class="sf-toolbar-value">{{ error_count ?: collector.countdefines }}</span>
<span class="sf-toolbar-value">{{ error_count ?: collector.countDefines }}</span>
{% endset %}
{% set text %}
@ -28,7 +28,7 @@
<div class="sf-toolbar-info-piece">
<b>Defined messages</b>
<span class="sf-toolbar-status">{{ collector.countdefines }}</span>
<span class="sf-toolbar-status">{{ collector.countDefines }}</span>
</div>
{% endset %}
@ -65,7 +65,7 @@
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.countdefines }}</span>
<span class="value">{{ collector.countDefines }}</span>
<span class="label">Defined messages</span>
</div>
@ -96,7 +96,7 @@
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Defined <span class="badge">{{ messages_defined|length }}</span></h3>
<h3 class="tab-title">Defined <span class="badge">{{ collector.countDefines }}</span></h3>
<div class="tab-content">
<p class="help">
@ -114,7 +114,7 @@
</div>
<div class="tab">
<h3 class="tab-title">Fallback <span class="badge">{{ messages_fallback|length }}</span></h3>
<h3 class="tab-title">Fallback <span class="badge {{ collector.countFallbacks ? 'status-warning' }}">{{ collector.countFallbacks }}</span></h3>
<div class="tab-content">
<p class="help">
@ -133,7 +133,7 @@
</div>
<div class="tab">
<h3 class="tab-title">Missing <span class="badge">{{ messages_missing|length }}</span></h3>
<h3 class="tab-title">Missing <span class="badge {{ collector.countMissings ? 'status-error' }}">{{ collector.countMissings }}</span></h3>
<div class="tab-content">
<p class="help">
@ -169,10 +169,10 @@
{% for message in messages %}
<tr>
<td class="font-normal text-small">{{ message.locale }}</td>
<td class="font-normal text-small text-bold">{{ message.domain }}</td>
<td class="font-normal text-small text-bold nowrap">{{ message.domain }}</td>
<td class="font-normal text-small">{{ message.count }}</td>
<td>
{{ message.id }}
<span class="nowrap">{{ message.id }}</span>
{% if message.transChoiceNumber is not null %}
<small class="newline">(pluralization is used)</small>
@ -188,7 +188,7 @@
</div>
{% endif %}
</td>
<td>{{ message.translation }}</td>
<td class="prewrap">{{ message.translation }}</td>
</tr>
{% endfor %}
</tbody>

View File

@ -204,6 +204,9 @@ table tbody ul {
.nowrap {
white-space: pre;
}
.prewrap {
white-space: pre-wrap;
}
.newline {
display: block;
}

View File

@ -446,6 +446,31 @@ class Filesystem
$startPathArr = explode('/', trim($startPath, '/'));
$endPathArr = explode('/', trim($endPath, '/'));
if ('/' !== $startPath[0]) {
array_shift($startPathArr);
}
if ('/' !== $endPath[0]) {
array_shift($endPathArr);
}
$normalizePathArray = function ($pathSegments) {
$result = array();
foreach ($pathSegments as $segment) {
if ('..' === $segment) {
array_pop($result);
} else {
$result[] = $segment;
}
}
return $result;
};
$startPathArr = $normalizePathArray($startPathArr);
$endPathArr = $normalizePathArray($endPathArr);
// Find for which directory the common path stops
$index = 0;
while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {

View File

@ -1117,6 +1117,16 @@ class FilesystemTest extends FilesystemTestCase
array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'),
array('/aab/bb', '/aa', '../aab/bb/'),
array('/aab', '/aa', '../aab/'),
array('/aa/bb/cc', '/aa/dd/..', 'bb/cc/'),
array('/aa/../bb/cc', '/aa/dd/..', '../bb/cc/'),
array('/aa/bb/../../cc', '/aa/../dd/..', 'cc/'),
array('/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'),
array('/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'),
array('C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'),
array('c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'),
array('C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'),
array('C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'),
array('C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'),
);
if ('\\' === DIRECTORY_SEPARATOR) {

View File

@ -201,6 +201,28 @@ class Response
$this->setContent($content);
$this->setStatusCode($status);
$this->setProtocolVersion('1.0');
// Deprecations
$class = get_class($this);
if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) {
$class = get_parent_class($class);
}
if (isset(self::$deprecationsTriggered[$class])) {
return;
}
self::$deprecationsTriggered[$class] = true;
foreach (self::$deprecatedMethods as $method) {
$r = new \ReflectionMethod($class, $method);
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Extending %s::%s() in %s is deprecated since version 3.2 and won\'t be supported anymore in 4.0 as it will be final.', __CLASS__, $method, $class), E_USER_DEPRECATED);
}
}
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!$this->headers->has('Date')) {
$this->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
}
}
/**
@ -329,6 +351,7 @@ class Response
return $this;
}
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}
@ -642,6 +665,11 @@ class Response
*/
public function getDate()
{
/*
RFC2616 - 14.18 says all Responses need to have a Date.
Make sure we provide one even if it the header
has been removed in the meantime.
*/
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}

View File

@ -58,9 +58,9 @@ class MockArraySessionStorage implements SessionStorageInterface
protected $metadataBag;
/**
* @var array
* @var array|SessionBagInterface[]
*/
protected $bags;
protected $bags = array();
/**
* Constructor.

View File

@ -276,8 +276,10 @@ class ResponseTest extends ResponseTestCase
$this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified');
$response = new Response('', 200);
$now = $this->createDateTimeNow();
$response->headers->remove('Date');
$this->assertInstanceOf('\DateTime', $response->getDate());
$date = $response->getDate();
$this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed');
}
public function testGetMaxAge()

View File

@ -97,6 +97,30 @@ class MockArraySessionStorageTest extends TestCase
$this->assertNotEquals('', $this->storage->getId());
}
public function testClearClearsBags()
{
$this->storage->clear();
$this->assertSame(array(), $this->storage->getBag('attributes')->all());
$this->assertSame(array(), $this->storage->getBag('flashes')->peekAll());
}
public function testClearStartsSession()
{
$this->storage->clear();
$this->assertTrue($this->storage->isStarted());
}
public function testClearWithNoBagsStartsSession()
{
$storage = new MockArraySessionStorage();
$storage->clear();
$this->assertTrue($storage->isStarted());
}
/**
* @expectedException \RuntimeException
*/

View File

@ -150,7 +150,7 @@ abstract class AbstractToken implements TokenInterface
array(
is_object($this->user) ? clone $this->user : $this->user,
$this->authenticated,
$this->roles,
array_map(function ($role) { return clone $role; }, $this->roles),
$this->attributes,
)
);

View File

@ -221,7 +221,7 @@ class UserAuthenticationProviderTest extends TestCase
$this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $authToken);
$this->assertSame($user, $authToken->getUser());
$this->assertContains(new Role('ROLE_FOO'), $authToken->getRoles(), '', false, false);
$this->assertContains($switchUserRole, $authToken->getRoles());
$this->assertContains($switchUserRole, $authToken->getRoles(), '', false, false);
$this->assertEquals('foo', $authToken->getCredentials());
$this->assertEquals(array('foo' => 'bar'), $authToken->getAttributes(), '->authenticate() copies token attributes');
}

View File

@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\SwitchUserRole;
use Symfony\Component\Security\Core\User\User;
class TestUser
{
@ -89,7 +90,7 @@ class AbstractTokenTest extends TestCase
public function testSerialize()
{
$token = $this->getToken(array('ROLE_FOO'));
$token = $this->getToken(array('ROLE_FOO', new Role('ROLE_BAR')));
$token->setAttributes(array('foo' => 'bar'));
$uToken = unserialize(serialize($token));
@ -98,6 +99,19 @@ class AbstractTokenTest extends TestCase
$this->assertEquals($token->getAttributes(), $uToken->getAttributes());
}
public function testSerializeWithRoleObjects()
{
$user = new User('name', 'password', array(new Role('ROLE_FOO'), new Role('ROLE_BAR')));
$token = new ConcreteToken($user, $user->getRoles());
$serialized = serialize($token);
$unserialized = unserialize($serialized);
$roles = $unserialized->getRoles();
$this->assertEquals($roles, $user->getRoles());
}
public function testSerializeParent()
{
$user = new TestUser('fabien');

View File

@ -33,7 +33,9 @@ class UrlValidator extends ConstraintValidator
\] # an IPv6 address
)
(:[0-9]+)? # a port (optional)
(/?|/\S+|\?\S*|\#\S*) # a /, nothing, a / with something, a query or a fragment
(?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path
(?:\? (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional)
(?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional)
$~ixu';
/**

View File

@ -123,6 +123,7 @@ class UrlValidatorTest extends ConstraintValidatorTestCase
array('http://symfony.com#'),
array('http://symfony.com#fragment'),
array('http://symfony.com/#fragment'),
array('http://symfony.com/#one_more%20test'),
);
}
@ -163,6 +164,9 @@ class UrlValidatorTest extends ConstraintValidatorTestCase
array('http://:password@@symfony.com'),
array('http://username:passwordsymfony.com'),
array('http://usern@me:password@symfony.com'),
array('http://example.com/exploit.html?<script>alert(1);</script>'),
array('http://example.com/exploit.html?hel lo'),
array('http://example.com/exploit.html?not_a%hex'),
);
}