8000 Merge branch '4.1' · symfony/symfony@a687119 · GitHub
[go: up one dir, main page]

Skip to content

Commit a687119

Browse files
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
2 parents b560883 + f658ed6 commit a687119

File tree

16 files changed

+237
-38
lines changed
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode =
5454
$instantiation = 'return';
5555

5656
if ($definition->isShared()) {
57-
$instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() || !method_exists(ContainerBuilder::class, 'addClassResource') ? 'services' : 'privates', $id);
57+
$instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', $id);
5858
}
5959

6060
if (null === $factoryCode) {
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,34 @@ public function testGetProxyFactoryCode()
8080
);
8181
}
8282

83+
/**
84+
* @dataProvider getPrivatePublicDefinitions
85+
*/
86+
public function testCorrectAssigning(Definition $definition, $access)
87+
{
88+
$definition->setLazy(true);
89+
90+
$code = $this->dumper->getProxyFactoryCode($definition, 'foo', '$this->getFoo2Service(false)');
91+
92+
$this->assertStringMatchesFormat('%A$this->'.$access.'[\'foo\'] = %A', $code);
93+
}
94+
95+
public function getPrivatePublicDefinitions()
96+
{
97+
return array(
98+
array(
99+
(new Definition(__CLASS__))
100+
->setPublic(false),
101+
'privates',
102+
),
103+
array(
104+
(new Definition(__CLASS__))
105+
->setPublic(true),
106+
'services',
107+
),
108+
);
109+
}
110+
83111
/**
84112
* @expectedException \InvalidArgumentException
85113
* @expectedExceptionMessage Missing factory code to construct the service "foo".
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass;
16+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
18+
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
20+
use Symfony\Component\DependencyInjection\ServiceLocator;
21+
use Symfony\Component\DependencyInjection\Reference;
22+
23+
class TestServiceContainerRefPassesTest extends TestCase
24+
{
25+
public function testProcess()
26+
{
27+
$container = new ContainerBuilder();
28+
$container->register('test.private_services_locator', ServiceLocator::class)
29+
->setPublic(true)
30+
->addArgument(0, array());
31+
32+
$container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32);
33+
$container->addCompilerPass(new TestServiceContainerRealRefPass(), PassConfig::TYPE_AFTER_REMOVING);
34+
35+
$container->register('Test\public_service')
36+
->setPublic(true)
37+
->addArgument(new Reference('Test\private_used_shared_service'))
38+
->addArgument(new Reference('Test\private_used_non_shared_service'))
39+
;
40+
41+
$container->register('Test\private_used_shared_service');
42+
$container->register('Test\private_unused_shared_service');
43+
$container->register('Test\private_used_non_shared_service')->setShared(false);
44+
$container->register('Test\private_unused_non_shared_service')->setShared(false);
45+
46+
$container->compile();
47+
48+
$expected = array(
49+
'Test\private_used_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_shared_service')),
50+
'Test\private_used_non_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_non_shared_service')),
51+
'Psr\Container\ContainerInterface' => new ServiceClosureArgument(new Reference('service_container')),
52+
'Symfony\Component\DependencyInjection\ContainerInterface' => new ServiceClosureArgument(new Reference('service_container')),
53+
);
54+
$this->assertEquals($expected, $container->getDefinition('test.private_services_locator')->getArgument(0));
55+
$this->assertSame($container, $container->get('test.private_services_locator')->get('Psr\Container\ContainerInterface'));
56+
$this->assertFalse($container->getDefinition('Test\private_used_non_shared_service')->isShared());
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"php": "^7.1.3",
2020
"ext-xml": "*",
2121
"symfony/cache": "~3.4|~4.0",
22-
"symfony/dependency-injection": "^4.1",
22+
"symfony/dependency-injection": "^4.1.1",
2323
"symfony/config": "~3.4|~4.0",
2424
"symfony/event-dispatcher": "^4.1",
2525
"symfony/http-foundation": "^4.1",
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class SecurityExtension extends Extension
4545
private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me');
4646
private $factories = array();
4747
private $userProviderFactories = array();
48+
private $statelessFirewallKeys = array();
4849

4950
public function __construct()
5051
{
@@ -104,6 +105,9 @@ public function load(array $configs, ContainerBuilder $container)
104105
$this->createAuthorization($config, $container);
105106
$this->createRoleHierarchy($config, $container);
106107

108+
$container->getDefinition('security.authentication.guard_handler')
109+
->replaceArgument(2, $this->statelessFirewallKeys);
110+
107111
if ($config['encoders']) {
108112
$this->createEncoders($config['encoders'], $container);
109113
}
@@ -287,6 +291,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
287291
$listeners[] = new Reference($this->createContextListener($container, $contextKey));
288292
$sessionStrategyId = 'security.authentication.session_strategy';
289293
} else {
294+
$this->statelessFirewallKeys[] = $id;
290295
$sessionStrategyId = 'security.authentication.session_strategy_noop';
291296
}
292297
$container->setAlias(new Alias('security.authentication.session_strategy.'.$id, false), $sessionStrategyId);
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
>
1313
<argument type="service" id="security.token_storage" />
1414
<argument type="service" id="event_dispatcher" on-invalid="null" />
15+
<argument /> <!-- stateless firewall keys -->
16+
<call method="setSessionAuthenticationStrategy">
17+
<argument type="service" id="security.authentication.session_strategy" />
18+
</call>
1519
</service>
1620

1721
<service id="Symfony\Component\Security\Guard\GuardAuthenticatorHandler" alias="security.authentication.guard_handler" />
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,35 @@ public function testDisableRoleHierarchyVoter()
123123
$this->assertFalse($container->hasDefinition('security.access.role_hierarchy_voter'));
124124
}
125125

126+
public function testGuardHandlerIsPassedStatelessFirewalls()
127+
{
128+
$container = $this->getRawContainer();
129+
130+
$container->loadFromExtension('security', array(
131+
'providers' => array(
132+
'default' => array('id' => 'foo'),
133+
),
134+
135+
'firewalls' => array(
136+
'some_firewall' => array(
137+
'pattern' => '^/admin',
138+
'http_basic' => null,
139+
'logout_on_user_change' => true,
140+
),
141+
'stateless_firewall' => array(
142+
'pattern' => '/.*',
143+
'stateless' => true,
144+
'http_basic' => null,
145+
'logout_on_user_change' => true,
146+
),
147+
),
148+
));
149+
150+
$container->compile();
151+
$definition = $container->getDefinition('security.authentication.guard_handler');
152+
$this->assertSame(array('stateless_firewall'), $definition->getArgument(2));
153+
}
154+
126155
public function testSwitchUserNotStatelessOnStatelessFirewall()
127156
{
128157
$container = $this->getRawContainer();
Original file line numberDiff line numberDiff line change
@@ -419,9 +419,10 @@
419419
function(xhr, el) {
420420
421421
/* Evaluate in global scope scripts embedded inside the toolbar */
422-
eval.call({}, ([].slice.call(el.querySelectorAll('script')).map(function (script) {
423-
return script.firstChild.nodeValue;
424-
}).join(';\n')));
422+
var i, scripts = [].slice.call(el.querySelectorAll('script'));
423+
for (i = 0; i < scripts.length; ++i) {
424+
eval.call({}, scripts[i].firstChild.nodeValue);
425+
}
425426
426427
el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none';
427428
@@ -440,7 +441,7 @@
440441
}
441442
442443
/* Handle toolbar-info position */
443-
var i, toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block'));
444+
var toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block'));
444445
for (i = 0; i < toolbarBlocks.length; ++i) {
445446
toolbarBlocks[i].onmouseover = function () {
446447
var toolbarInfo = this.querySelectorAll('.sf-toolbar-info')[0];
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ protected function doHave($id)
197197
protected function doClear($namespace)
198198
{
199199
// When using a native Redis cluster, clearing the cache is done by versioning in AbstractTrait::clear().
200-
// This means old keys are not really removed until they expire and may need gargage collection.
200+
// This means old keys are not really removed until they expire and may need garbage collection.
201201

202202
$cleared = true;
203203
$hosts = array($this->redis);
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ private function isInlineableDefinition($id, Definition $definition)
162162
}
163163

164164
if (!$definition->isShared()) {
165+
foreach ($graph->getNode($id)->getInEdges() as $edge) {
166+
if ($edge->isWeak()) {
167+
return false;
168+
}
169+
}
170+
165171
return true;
166172
}
167173

Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private function executeGuardAuthenticator($uniqueGuardKey, AuthenticatorInterfa
126126
}
127127

128128
// sets the token on the token storage, etc
129-
$this->guardHandler->authenticateWithToken($token, $request);
129+
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
130130
} catch (AuthenticationException $e) {
131131
// oh no! Authentication failed!
132132

Original file line numberDiff line numberDiff line change
@@ -37,19 +37,28 @@ class GuardAuthenticatorHandler
3737
private $tokenStorage;
3838
private $dispatcher;
3939
private $sessionStrategy;
40+
private $statelessProviderKeys;
4041

41-
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
42+
/**
43+
* @param array $statelessProviderKeys An array of provider/firewall keys that are "stateless" and so do not need the session migrated on success
44+
*/
45+
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null, array $statelessProviderKeys = array())
4246
{
4347
$this->tokenStorage = $tokenStorage;
4448
$this->dispatcher = $eventDispatcher;
49+
$this->statelessProviderKeys = $statelessProviderKeys;
4550
}
4651

4752
/**
4853
* Authenticates the given token in the system.
54+
*
55+
* @param string $providerKey The name of the provider/firewall being used for authentication
4956
*/
50-
public function authenticateWithToken(TokenInterface $token, Request $request)
57+
public function authenticateWithToken(TokenInterface $token, Request $request/*, string $providerKey */)
5158
{
52-
$this->migrateSession($request, $token);
59+
$providerKey = \func_num_args() > 2 ? func_get_arg(2) : null;
60+
61+
$this->migrateSession($request, $token, $providerKey);
5362
$this->tokenStorage->setToken($token);
5463

5564
if (null !== $this->dispatcher) {
@@ -86,7 +95,7 @@ public function authenticateUserAndHandleSuccess(UserInterface $user, Request $r
8695
// create an authenticated token for the User
8796
$token = $authenticator->createAuthenticatedToken($user, $providerKey);
8897
// authenticate this in the system
89-
$this->authenticateWithToken($token, $request);
98+
$this->authenticateWithToken($token, $request, $providerKey);
9099

91100
// return the success metric
92101
return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey);
@@ -121,9 +130,9 @@ public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyIn
121130
$this->sessionStrategy = $sessionStrategy;
122131
}
123132

124-
private function migrateSession(Request $request, TokenInterface $token)
133+
private function migrateSession(Request $request, TokenInterface $token, $providerKey)
125134
{
126-
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
135+
if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession() || \in_array($providerKey, $this->statelessProviderKeys, true)) {
127136
return;
128137
}
129138

Original file line numberDiff line numberDiff line change
@@ -144,6 +144,18 @@ public function testSessionStrategyIsCalled()
144144
$handler->authenticateWithToken($this->token, $this->request);
145145
}
146146

147+
public function testSessionStrategyIsNotCalledWhenStateless()
148+
{
149+
$this->configurePreviousSession();
150+
151+
$this->sessionStrategy->expects($this->never())
152+
->method('onAuthentication');
153+
154+
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher, array('some_provider_key'));
155+
$handler->setSessionAuthenticationStrategy($this->sessionStrategy);
156+
$handler->authenticateWithToken($this->token, $this->request, 'some_provider_key');
157+
}
158+
147159
protected function setUp()
148160
{
149161
$this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock();
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,12 @@ class SplCaster
2929

3030
public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested)
3131
{
32-
$prefix = Caster::PREFIX_VIRTUAL;
33-
$class = $stub->class;
34-
$flags = $c->getFlags();
35-
36-
$b = array(
37-
$prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
38-
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
39-
$prefix.'iteratorClass' => new ClassStub($c->getIteratorClass()),
40-
$prefix.'storage' => $c->getArrayCopy(),
41-
);
42-
43-
if ('ArrayObject' === $class) {
44-
$a = $b;
45-
} else {
46-
if (!($flags & \ArrayObject::STD_PROP_LIST)) {
47-
$c->setFlags(\ArrayObject::STD_PROP_LIST);
48-
$a = Caster::castObject($c, $class);
49-
$c->setFlags($flags);
50-
}
51-
52-
$a += $b;
53-
}
32+
return self::castSplArray($c, $a, $stub, $isNested);
33+
}
5434

55-
return $a;
35+
public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, $isNested)
36+
{
37+
return self::castSplArray($c, $a, $stub, $isNested);
5638
}
5739

5840
public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested)
@@ -186,7 +168,7 @@ public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $s
186168

187169
$clone = clone $c;
188170
foreach ($clone as $obj) {
189-
$storage[spl_object_hash($obj)] = array(
171+
$storage[] = array(
190172
'object' => $obj,
191173
'info' => $clone->getInfo(),
192174
);
@@ -205,4 +187,27 @@ public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub
205187

206188
return $a;
207189
}
190+
191+
private static function castSplArray($c, array $a, Stub $stub, $isNested)
192+
{
193+
$prefix = Caster::PREFIX_VIRTUAL;
194+
$class = $stub->class;
195+
$flags = $c->getFlags();
196+
197+
if (!($flags & \ArrayObject::STD_PROP_LIST)) {
198+
$c->setFlags(\ArrayObject::STD_PROP_LIST);
199+
$a = Caster::castObject($c, $class);
200+
$c->setFlags($flags);
201+
}
202+
$a += array(
203+
$prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST),
204+
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
205+
);
206+
if ($c instanceof \ArrayObject) {
207+
$a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass());
208+
}
209+
$a[$prefix.'storage'] = $c->getArrayCopy();
210+
211+
return $a;
212+
}
208213
}
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ abstract class AbstractCloner implements ClonerInterface
9595
'AMQPEnvelope' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'),
9696

9797
'ArrayObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'),
98+
'ArrayIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'),
9899
'SplDoublyLinkedList' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'),
99100
'SplFileInfo' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'),
100101
'SplFileObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'),