Merge branch '4.1'
* 4.1: remove HHVM code [VarDumper] Fix dumping ArrayObject and ArrayIterator instances [ProxyManagerBridge] Fixed support of private services [Cache] Fix typo in comment. [FrameworkBundle] give access to non-shared services when using test.service_container Fix bad method call with guard authentication + session migration Avoid calling eval when there is no script embedded in the toolbar
This commit is contained in:
commit
a687119e10
@ -54,7 +54,7 @@ class ProxyDumper implements DumperInterface
|
||||
$instantiation = 'return';
|
||||
|
||||
if ($definition->isShared()) {
|
||||
$instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() || !method_exists(ContainerBuilder::class, 'addClassResource') ? 'services' : 'privates', $id);
|
||||
$instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', $id);
|
||||
}
|
||||
|
||||
if (null === $factoryCode) {
|
||||
|
@ -80,6 +80,34 @@ class ProxyDumperTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPrivatePublicDefinitions
|
||||
*/
|
||||
public function testCorrectAssigning(Definition $definition, $access)
|
||||
{
|
||||
$definition->setLazy(true);
|
||||
|
||||
$code = $this->dumper->getProxyFactoryCode($definition, 'foo', '$this->getFoo2Service(false)');
|
||||
|
||||
$this->assertStringMatchesFormat('%A$this->'.$access.'[\'foo\'] = %A', $code);
|
||||
}
|
||||
|
||||
public function getPrivatePublicDefinitions()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
(new Definition(__CLASS__))
|
||||
->setPublic(false),
|
||||
'privates',
|
||||
),
|
||||
array(
|
||||
(new Definition(__CLASS__))
|
||||
->setPublic(true),
|
||||
'services',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Missing factory code to construct the service "foo".
|
||||
|
@ -0,0 +1,58 @@
|
||||
<?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\DependencyInjection\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class TestServiceContainerRefPassesTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('test.private_services_locator', ServiceLocator::class)
|
||||
->setPublic(true)
|
||||
->addArgument(0, array());
|
||||
|
||||
$container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);
|
||||
$container->addCompilerPass(new TestServiceContainerRealRefPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
|
||||
$container->register('Test\public_service')
|
||||
->setPublic(true)
|
||||
->addArgument(new Reference('Test\private_used_shared_service'))
|
||||
->addArgument(new Reference('Test\private_used_non_shared_service'))
|
||||
;
|
||||
|
||||
$container->register('Test\private_used_shared_service');
|
||||
$container->register('Test\private_unused_shared_service');
|
||||
$container->register('Test\private_used_non_shared_service')->setShared(false);
|
||||
$container->register('Test\private_unused_non_shared_service')->setShared(false);
|
||||
|
||||
$container->compile();
|
||||
|
||||
$expected = array(
|
||||
'Test\private_used_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_shared_service')),
|
||||
'Test\private_used_non_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_non_shared_service')),
|
||||
'Psr\Container\ContainerInterface' => new ServiceClosureArgument(new Reference('service_container')),
|
||||
'Symfony\Component\DependencyInjection\ContainerInterface' => new ServiceClosureArgument(new Reference('service_container')),
|
||||
);
|
||||
$this->assertEquals($expected, $container->getDefinition('test.private_services_locator')->getArgument(0));
|
||||
$this->assertSame($container, $container->get('test.private_services_locator')->get('Psr\Container\ContainerInterface'));
|
||||
$this->assertFalse($container->getDefinition('Test\private_used_non_shared_service')->isShared());
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
"php": "^7.1.3",
|
||||
"ext-xml": "*",
|
||||
"symfony/cache": "~3.4|~4.0",
|
||||
"symfony/dependency-injection": "^4.1",
|
||||
"symfony/dependency-injection": "^4.1.1",
|
||||
"symfony/config": "~3.4|~4.0",
|
||||
"symfony/event-dispatcher": "^4.1",
|
||||
"symfony/http-foundation": "^4.1",
|
||||
|
@ -45,6 +45,7 @@ class SecurityExtension extends Extension
|
||||
private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me');
|
||||
private $factories = array();
|
||||
private $userProviderFactories = array();
|
||||
private $statelessFirewallKeys = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -104,6 +105,9 @@ class SecurityExtension extends Extension
|
||||
$this->createAuthorization($config, $container);
|
||||
$this->createRoleHierarchy($config, $container);
|
||||
|
||||
$container->getDefinition('security.authentication.guard_handler')
|
||||
->replaceArgument(2, $this->statelessFirewallKeys);
|
||||
|
||||
if ($config['encoders']) {
|
||||
$this->createEncoders($config['encoders'], $container);
|
||||
}
|
||||
@ -287,6 +291,7 @@ class SecurityExtension extends Extension
|
||||
$listeners[] = new Reference($this->createContextListener($container, $contextKey));
|
||||
$sessionStrategyId = 'security.authentication.session_strategy';
|
||||
} else {
|
||||
$this->statelessFirewallKeys[] = $id;
|
||||
$sessionStrategyId = 'security.authentication.session_strategy_noop';
|
||||
}
|
||||
$container->setAlias(new Alias('security.authentication.session_strategy.'.$id, false), $sessionStrategyId);
|
||||
|
@ -12,6 +12,10 @@
|
||||
>
|
||||
<argument type="service" id="security.token_storage" />
|
||||
<argument type="service" id="event_dispatcher" on-invalid="null" />
|
||||
<argument /> <!-- stateless firewall keys -->
|
||||
<call method="setSessionAuthenticationStrategy">
|
||||
<argument type="service" id="security.authentication.session_strategy" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="Symfony\Component\Security\Guard\GuardAuthenticatorHandler" alias="security.authentication.guard_handler" />
|
||||
|
@ -123,6 +123,35 @@ class SecurityExtensionTest extends TestCase
|
||||
$this->assertFalse($container->hasDefinition('security.access.role_hierarchy_voter'));
|
||||
}
|
||||
|
||||
public function testGuardHandlerIsPassedStatelessFirewalls()
|
||||
{
|
||||
$container = $this->getRawContainer();
|
||||
|
||||
$container->loadFromExtension('security', array(
|
||||
'providers' => array(
|
||||
'default' => array('id' => 'foo'),
|
||||
),
|
||||
|
||||
'firewalls' => array(
|
||||
'some_firewall' => array(
|
||||
'pattern' => '^/admin',
|
||||
'http_basic' => null,
|
||||
'logout_on_user_change' => true,
|
||||
),
|
||||
'stateless_firewall' => array(
|
||||
'pattern' => '/.*',
|
||||
'stateless' => true,
|
||||
'http_basic' => null,
|
||||
'logout_on_user_change' => true,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$container->compile();
|
||||
$definition = $container->getDefinition('security.authentication.guard_handler');
|
||||
$this->assertSame(array('stateless_firewall'), $definition->getArgument(2));
|
||||
}
|
||||
|
||||
public function testSwitchUserNotStatelessOnStatelessFirewall()
|
||||
{
|
||||
$container = $this->getRawContainer();
|
||||
|
@ -419,9 +419,10 @@
|
||||
function(xhr, el) {
|
||||
|
||||
/* Evaluate in global scope scripts embedded inside the toolbar */
|
||||
eval.call({}, ([].slice.call(el.querySelectorAll('script')).map(function (script) {
|
||||
return script.firstChild.nodeValue;
|
||||
}).join(';\n')));
|
||||
var i, scripts = [].slice.call(el.querySelectorAll('script'));
|
||||
for (i = 0; i < scripts.length; ++i) {
|
||||
eval.call({}, scripts[i].firstChild.nodeValue);
|
||||
}
|
||||
|
||||
el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none';
|
||||
|
||||
@ -440,7 +441,7 @@
|
||||
}
|
||||
|
||||
/* Handle toolbar-info position */
|
||||
var i, toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block'));
|
||||
var toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block'));
|
||||
for (i = 0; i < toolbarBlocks.length; ++i) {
|
||||
toolbarBlocks[i].onmouseover = function () {
|
||||
var toolbarInfo = this.querySelectorAll('.sf-toolbar-info')[0];
|
||||
|
@ -197,7 +197,7 @@ trait RedisTrait
|
||||
protected function doClear($namespace)
|
||||
{
|
||||
// When using a native Redis cluster, clearing the cache is done by versioning in AbstractTrait::clear().
|
||||
// This means old keys are not really removed until they expire and may need gargage collection.
|
||||
// This means old keys are not really removed until they expire and may need garbage collection.
|
||||
|
||||
$cleared = true;
|
||||
$hosts = array($this->redis);
|
||||
|
@ -162,6 +162,12 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements Repe
|
||||
}
|
||||
|
||||
if (!$definition->isShared()) {
|
||||
foreach ($graph->getNode($id)->getInEdges() as $edge) {
|
||||
if ($edge->isWeak()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ class GuardAuthenticationListener implements ListenerInterface
|
||||
}
|
||||
|
||||
// sets the token on the token storage, etc
|
||||
$this->guardHandler->authenticateWithToken($token, $request);
|
||||
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
|
||||
} catch (AuthenticationException $e) {
|
||||
// oh no! Authentication failed!
|
||||
|
||||
|
@ -37,19 +37,28 @@ class GuardAuthenticatorHandler
|
||||
private $tokenStorage;
|
||||
private $dispatcher;
|
||||
private $sessionStrategy;
|
||||
private $statelessProviderKeys;
|
||||
|
||||
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
|
||||
/**
|
||||
* @param array $statelessProviderKeys An array of provider/firewall keys that are "stateless" and so do not need the session migrated on success
|
||||
*/
|
||||
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null, array $statelessProviderKeys = array())
|
||||
{
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->dispatcher = $eventDispatcher;
|
||||
$this->statelessProviderKeys = $statelessProviderKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates the given token in the system.
|
||||
*
|
||||
* @param string $providerKey The name of the provider/firewall being used for authentication
|
||||
*/
|
||||
public function authenticateWithToken(TokenInterface $token, Request $request)
|
||||
public function authenticateWithToken(TokenInterface $token, Request $request/*, string $providerKey */)
|
||||
{
|
||||
$this->migrateSession($request, $token);
|
||||
$providerKey = \func_num_args() > 2 ? func_get_arg(2) : null;
|
||||
|
||||
$this->migrateSession($request, $token, $providerKey);
|
||||
$this->tokenStorage->setToken($token);
|
||||
|
||||
if (null !== $this->dispatcher) {
|
||||
@ -86,7 +95,7 @@ class GuardAuthenticatorHandler
|
||||
// create an authenticated token for the User
|
||||
$token = $authenticator->createAuthenticatedToken($user, $providerKey);
|
||||
// authenticate this in the system
|
||||
$this->authenticateWithToken($token, $request);
|
||||
$this->authenticateWithToken($token, $request, $providerKey);
|
||||
|
||||
// return the success metric
|
||||
return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey);
|
||||
@ -121,9 +130,9 @@ class GuardAuthenticatorHandler
|
||||
$this->sessionStrategy = $sessionStrategy;
|
||||
}
|
||||
|
||||
private function migrateSession(Request $request, TokenInterface $token)
|
||||
private function migrateSession(Request $request, TokenInterface $token, $providerKey)
|
||||
{
|
||||
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
|
||||
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession() || \in_array($providerKey, $this->statelessProviderKeys, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,18 @@ class GuardAuthenticatorHandlerTest extends TestCase
|
||||
$handler->authenticateWithToken($this->token, $this->request);
|
||||
}
|
||||
|
||||
public function testSessionStrategyIsNotCalledWhenStateless()
|
||||
{
|
||||
$this->configurePreviousSession();
|
||||
|
||||
$this->sessionStrategy->expects($this->never())
|
||||
->method('onAuthentication');
|
||||
|
||||
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher, array('some_provider_key'));
|
||||
$handler->setSessionAuthenticationStrategy($this->sessionStrategy);
|
||||
$handler->authenticateWithToken($this->token, $this->request, 'some_provider_key');
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock();
|
||||
|
@ -29,30 +29,12 @@ class SplCaster
|
||||
|
||||
public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested)
|
||||
{
|
||||
$prefix = Caster::PREFIX_VIRTUAL;
|
||||
$class = $stub->class;
|
||||
$flags = $c->getFlags();
|
||||
return self::castSplArray($c, $a, $stub, $isNested);
|
||||
}
|
||||
|
||||
$b = array(
|
||||
$prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
|
||||
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
|
||||
$prefix.'iteratorClass' => new ClassStub($c->getIteratorClass()),
|
||||
$prefix.'storage' => $c->getArrayCopy(),
|
||||
);
|
||||
|
||||
if ('ArrayObject' === $class) {
|
||||
$a = $b;
|
||||
} else {
|
||||
if (!($flags & \ArrayObject::STD_PROP_LIST)) {
|
||||
$c->setFlags(\ArrayObject::STD_PROP_LIST);
|
||||
$a = Caster::castObject($c, $class);
|
||||
$c->setFlags($flags);
|
||||
}
|
||||
|
||||
$a += $b;
|
||||
}
|
||||
|
||||
return $a;
|
||||
public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, $isNested)
|
||||
{
|
||||
return self::castSplArray($c, $a, $stub, $isNested);
|
||||
}
|
||||
|
||||
public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested)
|
||||
@ -186,7 +168,7 @@ class SplCaster
|
||||
|
||||
$clone = clone $c;
|
||||
foreach ($clone as $obj) {
|
||||
$storage[spl_object_hash($obj)] = array(
|
||||
$storage[] = array(
|
||||
'object' => $obj,
|
||||
'info' => $clone->getInfo(),
|
||||
);
|
||||
@ -205,4 +187,27 @@ class SplCaster
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
private static function castSplArray($c, array $a, Stub $stub, $isNested)
|
||||
{
|
||||
$prefix = Caster::PREFIX_VIRTUAL;
|
||||
$class = $stub->class;
|
||||
$flags = $c->getFlags();
|
||||
|
||||
if (!($flags & \ArrayObject::STD_PROP_LIST)) {
|
||||
$c->setFlags(\ArrayObject::STD_PROP_LIST);
|
||||
$a = Caster::castObject($c, $class);
|
||||
$c->setFlags($flags);
|
||||
}
|
||||
$a += array(
|
||||
$prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
|
||||
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
|
||||
);
|
||||
if ($c instanceof \ArrayObject) {
|
||||
$a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass());
|
||||
}
|
||||
$a[$prefix.'storage'] = $c->getArrayCopy();
|
||||
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ abstract class AbstractCloner implements ClonerInterface
|
||||
'AMQPEnvelope' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'),
|
||||
|
||||
'ArrayObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'),
|
||||
'ArrayIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'),
|
||||
'SplDoublyLinkedList' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'),
|
||||
'SplFileInfo' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'),
|
||||
'SplFileObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'),
|
||||
|
@ -163,4 +163,45 @@ EOTXT;
|
||||
|
||||
$this->assertDumpMatchesFormat('%ADateTime%A', $var);
|
||||
}
|
||||
|
||||
public function testCastArrayObject()
|
||||
{
|
||||
$var = new \ArrayObject(array(123));
|
||||
$var->foo = 234;
|
||||
|
||||
$expected = <<<EOTXT
|
||||
ArrayObject {
|
||||
+"foo": 234
|
||||
flag::STD_PROP_LIST: false
|
||||
flag::ARRAY_AS_PROPS: false
|
||||
iteratorClass: "ArrayIterator"
|
||||
storage: array:1 [
|
||||
0 => 123
|
||||
]
|
||||
}
|
||||
EOTXT;
|
||||
$this->assertDumpEquals($expected, $var);
|
||||
}
|
||||
|
||||
public function testArrayIterator()
|
||||
{
|
||||
$var = new MyArrayIterator(array(234));
|
||||
|
||||
$expected = <<<EOTXT
|
||||
Symfony\Component\VarDumper\Tests\Caster\MyArrayIterator {
|
||||
-foo: 123
|
||||
flag::STD_PROP_LIST: false
|
||||
flag::ARRAY_AS_PROPS: false
|
||||
storage: array:1 [
|
||||
0 => 234
|
||||
]
|
||||
}
|
||||
EOTXT;
|
||||
$this->assertDumpEquals($expected, $var);
|
||||
}
|
||||
}
|
||||
|
||||
class MyArrayIterator extends \ArrayIterator
|
||||
{
|
||||
private $foo = 123;
|
||||
}
|
||||
|
Reference in New Issue
Block a user