3333use Symfony \Component \Security \Core \User \InMemoryUser ;
3434use Symfony \Component \Security \Http \FirewallMapInterface ;
3535use Symfony \Component \Security \Http \Logout \LogoutUrlGenerator ;
36+ use Symfony \Component \VarDumper \Caster \ClassStub ;
3637use Symfony \Contracts \EventDispatcher \EventDispatcherInterface ;
3738
3839class SecurityDataCollectorTest extends TestCase
@@ -220,46 +221,111 @@ public function testGetListeners()
220221 $ this ->assertSame (1 , $ listenerCalled );
221222 }
222223
223- public static function providerCollectDecisionLog (): \ Generator
224+ public function testCollectCollectsDecisionLogWhenStrategyIsAffirmative ()
224225 {
225226 $ voter1 = new DummyVoter ();
226227 $ voter2 = new DummyVoter ();
227228
228- $ eventDispatcher = new class () implements EventDispatcherInterface {
229+ $ decoratedVoter1 = new TraceableVoter ( $ voter1 , new class () implements EventDispatcherInterface {
229230 public function dispatch (object $ event , string $ eventName = null ): object
230231 {
231232 return new \stdClass ();
232233 }
233- };
234- $ decoratedVoter1 = new TraceableVoter ($ voter1 , $ eventDispatcher );
234+ });
235+
236+ $ strategy = MainConfiguration::STRATEGY_AFFIRMATIVE ;
237+
238+ $ accessDecisionManager = $ this ->createMock (TraceableAccessDecisionManager::class);
239+
240+ $ accessDecisionManager
241+ ->method ('getStrategy ' )
242+ ->willReturn ($ strategy );
235243
236- yield [
237- MainConfiguration::STRATEGY_AFFIRMATIVE ,
238- [[
244+ $ accessDecisionManager
245+ ->method ('getVoters ' )
246+ ->willReturn ([
247+ $ decoratedVoter1 ,
248+ $ decoratedVoter1 ,
249+ ]);
250+
251+ $ accessDecisionManager
252+ ->method ('getDecisionLog ' )
253+ ->willReturn ([[
239254 'attributes ' => ['view ' ],
240255 'object ' => new \stdClass (),
241256 'result ' => true ,
242257 'voterDetails ' => [
243258 ['voter ' => $ voter1 , 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_ABSTAIN ],
244259 ['voter ' => $ voter2 , 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_ABSTAIN ],
245260 ],
246- ]],
247- [$ decoratedVoter1 , $ decoratedVoter1 ],
248- [$ voter1 ::class, $ voter2 ::class],
249- [[
250- 'attributes ' => ['view ' ],
251- 'object ' => new \stdClass (),
252- 'result ' => true ,
253- 'voter_details ' => [
254- ['class ' => $ voter1 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_ABSTAIN ],
255- ['class ' => $ voter2 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_ABSTAIN ],
256- ],
257- ]],
261+ ]]);
262+
263+ $ dataCollector = new SecurityDataCollector (null , null , null , $ accessDecisionManager , null , null , true );
264+
265+ $ dataCollector ->collect (new Request (), new Response ());
266+
267+ $ actualDecisionLog = $ dataCollector ->getAccessDecisionLog ();
268+
269+ $ expectedDecisionLog = [[
270+ 'attributes ' => ['view ' ],
271+ 'object ' => new \stdClass (),
272+ 'result ' => true ,
273+ 'voter_details ' => [
274+ ['class ' => $ voter1 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_ABSTAIN ],
275+ ['class ' => $ voter2 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_ABSTAIN ],
276+ ],
277+ ]];
278+
279+ $ this ->assertEquals ($ actualDecisionLog , $ expectedDecisionLog , 'Wrong value returned by getAccessDecisionLog ' );
280+
281+ $ actualVoterClasses = array_map (static function (ClassStub $ classStub ): string {
282+ return (string ) $ classStub ;
283+ }, $ dataCollector ->getVoters ());
284+
285+ $ expectedVoterClasses = [
286+ $ voter1 ::class,
287+ $ voter2 ::class,
258288 ];
259289
260- yield [
261- MainConfiguration::STRATEGY_UNANIMOUS ,
262- [
290+ $ this ->assertSame (
291+ $ actualVoterClasses ,
292+ $ expectedVoterClasses ,
293+ 'Wrong value returned by getVoters '
294+ );
295+
296+ $ this ->assertSame ($ dataCollector ->getVoterStrategy (), $ strategy , 'Wrong value returned by getVoterStrategy ' );
297+ }
298+
299+ public function testCollectCollectsDecisionLogWhenStrategyIsUnanimous ()
300+ {
301+ $ voter1 = new DummyVoter ();
302+ $ voter2 = new DummyVoter ();
303+
304+ $ decoratedVoter1 = new TraceableVoter ($ voter1 , new class () implements EventDispatcherInterface {
305+ public function dispatch (object $ event , string $ eventName = null ): object
306+ {
307+ return new \stdClass ();
308+ }
309+ });
310+
311+ $ strategy = MainConfiguration::STRATEGY_UNANIMOUS ;
312+
313+ $ accessDecisionManager = $ this ->createMock (TraceableAccessDecisionManager::class);
314+
315+ $ accessDecisionManager
316+ ->method ('getStrategy ' )
317+ ->willReturn ($ strategy );
318+
319+ $ accessDecisionManager
320+ ->method ('getVoters ' )
321+ ->willReturn ([
322+ $ decoratedVoter1 ,
323+ $ decoratedVoter1 ,
324+ ]);
325+
326+ $ accessDecisionManager
327+ ->method ('getDecisionLog ' )
328+ ->willReturn ([
263329 [
264330 'attributes ' => ['view ' , 'edit ' ],
265331 'object ' => new \stdClass (),
@@ -280,78 +346,54 @@ public function dispatch(object $event, string $eventName = null): object
280346 ['voter ' => $ voter2 , 'attributes ' => ['update ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
281347 ],
282348 ],
283- ],
284- [$ decoratedVoter1 , $ decoratedVoter1 ],
285- [$ voter1 ::class, $ voter2 ::class],
349+ ]);
350+
351+ $ dataCollector = new SecurityDataCollector (null , null , null , $ accessDecisionManager , null , null , true );
352+
353+ $ dataCollector ->collect (new Request (), new Response ());
354+
355+ $ actualDecisionLog = $ dataCollector ->getAccessDecisionLog ();
356+
357+ $ expectedDecisionLog = [
286358 [
287- [
288- 'attributes ' => ['view ' , 'edit ' ],
289- 'object ' => new \stdClass (),
290- 'result ' => false ,
291- 'voter_details ' => [
292- ['class ' => $ voter1 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_DENIED ],
293- ['class ' => $ voter1 ::class, 'attributes ' => ['edit ' ], 'vote ' => VoterInterface::ACCESS_DENIED ],
294- ['class ' => $ voter2 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
295- ['class ' => $ voter2 ::class, 'attributes ' => ['edit ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
296- ],
359+ 'attributes ' => ['view ' , 'edit ' ],
360+ 'object ' => new \stdClass (),
361+ 'result ' => false ,
362+ 'voter_details ' => [
363+ ['class ' => $ voter1 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_DENIED ],
364+ ['class ' => $ voter1 ::class, 'attributes ' => ['edit ' ], 'vote ' => VoterInterface::ACCESS_DENIED ],
365+ ['class ' => $ voter2 ::class, 'attributes ' => ['view ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
366+ ['class ' => $ voter2 ::class, 'attributes ' => ['edit ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
297367 ],
298- [
299- ' attributes ' => [ ' update ' ],
300- ' object ' => new \ stdClass () ,
301- ' result ' => true ,
302- ' voter_details ' => [
303- [ ' class ' => $ voter1 ::class, ' attributes ' => [ ' update ' ], ' vote ' => VoterInterface:: ACCESS_GRANTED ],
304- ['class ' => $ voter2 ::class, 'attributes ' => ['update ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
305- ],
368+ ],
369+ [
370+ ' attributes ' => [ ' update ' ] ,
371+ ' object ' => new \ stdClass () ,
372+ ' result ' => true ,
373+ ' voter_details ' => [
374+ ['class ' => $ voter1 ::class, 'attributes ' => ['update ' ], 'vote ' => VoterInterface::ACCESS_GRANTED ],
375+ [ ' class ' => $ voter2 ::class, ' attributes ' => [ ' update ' ], ' vote ' => VoterInterface:: ACCESS_GRANTED ],
306376 ],
307377 ],
308378 ];
309- }
310379
311- /**
312- * Test the returned data when AccessDecisionManager is a TraceableAccessDecisionManager.
313- *
314- * @param string $strategy strategy returned by the AccessDecisionManager
315- * @param array $voters voters returned by AccessDecisionManager
316- * @param array $decisionLog log of the votes and final decisions from AccessDecisionManager
317- * @param array $expectedVoterClasses expected voter classes returned by the collector
318- * @param array $expectedDecisionLog expected decision log returned by the collector
319- *
320- * @dataProvider providerCollectDecisionLog
321- */
322- public function testCollectDecisionLog (string $ strategy , array $ decisionLog , array $ voters , array $ expectedVoterClasses , array $ expectedDecisionLog )
323- {
324- $ accessDecisionManager = $ this
325- ->getMockBuilder (TraceableAccessDecisionManager::class)
326- ->disableOriginalConstructor ()
327- ->setMethods (['getStrategy ' , 'getVoters ' , 'getDecisionLog ' ])
328- ->getMock ();
380+ $ this ->assertEquals ($ actualDecisionLog , $ expectedDecisionLog , 'Wrong value returned by getAccessDecisionLog ' );
329381
330- $ accessDecisionManager
331- ->expects ($ this ->any ())
332- ->method ('getStrategy ' )
333- ->willReturn ($ strategy );
382+ $ actualVoterClasses = array_map (static function (ClassStub $ classStub ): string {
383+ return (string ) $ classStub ;
384+ }, $ dataCollector ->getVoters ());
334385
335- $ accessDecisionManager
336- ->expects ($ this ->any ())
337- ->method ('getVoters ' )
338- ->willReturn ($ voters );
339-
340- $ accessDecisionManager
341- ->expects ($ this ->any ())
342- ->method ('getDecisionLog ' )
343- ->willReturn ($ decisionLog );
344-
345- $ dataCollector = new SecurityDataCollector (null , null , null , $ accessDecisionManager , null , null , true );
346- $ dataCollector ->collect (new Request (), new Response ());
347-
348- $ this ->assertEquals ($ dataCollector ->getAccessDecisionLog (), $ expectedDecisionLog , 'Wrong value returned by getAccessDecisionLog ' );
386+ $ expectedVoterClasses = [
387+ $ voter1 ::class,
388+ $ voter2 ::class,
389+ ];
349390
350391 $ this ->assertSame (
351- array_map ( function ( $ classStub ) { return ( string ) $ classStub ; }, $ dataCollector -> getVoters ()) ,
392+ $ actualVoterClasses ,
352393 $ expectedVoterClasses ,
353394 'Wrong value returned by getVoters '
354395 );
396+
355397 $ this ->assertSame ($ dataCollector ->getVoterStrategy (), $ strategy , 'Wrong value returned by getVoterStrategy ' );
356398 }
357399
@@ -387,7 +429,7 @@ private function getRoleHierarchy()
387429 }
388430}
389431
390- class DummyVoter implements VoterInterface
432+ final class DummyVoter implements VoterInterface
391433{
392434 public function vote (TokenInterface $ token , mixed $ subject , array $ attributes ): int
393435 {
0 commit comments