Skip to content

Commit c946ca0

Browse files
destillatlyrixx
authored andcommitted
[FrameworkBundle] fixed guard event names for transitions
1 parent bb96b28 commit c946ca0

File tree

3 files changed

+102
-6
lines changed

3 files changed

+102
-6
lines changed

EventListener/GuardExpression.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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\Component\Workflow\EventListener;
13+
14+
use Symfony\Component\Workflow\Transition;
15+
16+
class GuardExpression
17+
{
18+
private $transition;
19+
20+
private $expression;
21+
22+
public function getTransition(): Transition
23+
{
24+
return $this->transition;
25+
}
26+
27+
public function getExpression(): string
28+
{
29+
return $this->expression;
30+
}
31+
32+
public function __construct(Transition $transition, string $expression)
33+
{
34+
$this->transition = $transition;
35+
$this->expression = $expression;
36+
}
37+
}

EventListener/GuardListener.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Validator\Validator\ValidatorInterface;
1919
use Symfony\Component\Workflow\Event\GuardEvent;
2020
use Symfony\Component\Workflow\Exception\InvalidTokenConfigurationException;
21+
use Symfony\Component\Workflow\TransitionBlocker;
2122

2223
/**
2324
* @author Grégoire Pineau <lyrixx@lyrixx.info>
@@ -32,7 +33,7 @@ class GuardListener
3233
private $roleHierarchy;
3334
private $validator;
3435

35-
public function __construct($configuration, ExpressionLanguage $expressionLanguage, TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authenticationChecker, AuthenticationTrustResolverInterface $trustResolver, RoleHierarchyInterface $roleHierarchy = null, ValidatorInterface $validator = null)
36+
public function __construct(array $configuration, ExpressionLanguage $expressionLanguage, TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authenticationChecker, AuthenticationTrustResolverInterface $trustResolver, RoleHierarchyInterface $roleHierarchy = null, ValidatorInterface $validator = null)
3637
{
3738
$this->configuration = $configuration;
3839
$this->expressionLanguage = $expressionLanguage;
@@ -49,13 +50,29 @@ public function onTransition(GuardEvent $event, $eventName)
4950
return;
5051
}
5152

52-
if (!$this->expressionLanguage->evaluate($this->configuration[$eventName], $this->getVariables($event))) {
53-
$event->setBlocked(true);
53+
$eventConfiguration = (array) $this->configuration[$eventName];
54+
foreach ($eventConfiguration as $guard) {
55+
if ($guard instanceof GuardExpression) {
56+
if ($guard->getTransition() !== $event->getTransition()) {
57+
continue;
58+
}
59+
$this->validateGuardExpression($event, $guard->getExpression());
60+
} else {
61+
$this->validateGuardExpression($event, $guard);
62+
}
63+
}
64+
}
65+
66+
private function validateGuardExpression(GuardEvent $event, string $expression)
67+
{
68+
if (!$this->expressionLanguage->evaluate($expression, $this->getVariables($event))) {
69+
$blocker = TransitionBlocker::createBlockedByExpressionGuardListener($expression);
70+
$event->addTransitionBlocker($blocker);
5471
}
5572
}
5673

5774
// code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
58-
private function getVariables(GuardEvent $event)
75+
private function getVariables(GuardEvent $event): array
5976
{
6077
$token = $this->tokenStorage->getToken();
6178

Tests/EventListener/GuardListenerTest.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Symfony\Component\Validator\Validator\ValidatorInterface;
1212
use Symfony\Component\Workflow\Event\GuardEvent;
1313
use Symfony\Component\Workflow\EventListener\ExpressionLanguage;
14+
use Symfony\Component\Workflow\EventListener\GuardExpression;
1415
use Symfony\Component\Workflow\EventListener\GuardListener;
1516
use Symfony\Component\Workflow\Marking;
1617
use Symfony\Component\Workflow\Transition;
@@ -20,12 +21,17 @@ class GuardListenerTest extends TestCase
2021
private $authenticationChecker;
2122
private $validator;
2223
private $listener;
24+
private $transition;
2325

2426
protected function setUp()
2527
{
2628
$configuration = array(
2729
'test_is_granted' => 'is_granted("something")',
2830
'test_is_valid' => 'is_valid(subject)',
31+
'test_expression' => array(
32+
new GuardExpression($this->getTransition(true), '!is_valid(subject)'),
33+
new GuardExpression($this->getTransition(true), 'is_valid(subject)'),
34+
),
2935
);
3036
$expressionLanguage = new ExpressionLanguage();
3137
$token = $this->getMockBuilder(TokenInterface::class)->getMock();
@@ -96,11 +102,38 @@ public function testWithValidatorSupportedEventAndAccept()
96102
$this->assertFalse($event->isBlocked());
97103
}
98104

99-
private function createEvent()
105+
public function testWithGuardExpressionWithNotSupportedTransition()
106+
{
107+
$event = $this->createEvent(true);
108+
$this->configureValidator(false, false);
109+
$this->listener->onTransition($event, 'test_expression');
110+
111+
$this->assertFalse($event->isBlocked());
112+
}
113+
114+
public function testWithGuardExpressionWithSupportedTransition()
115+
{
116+
$event = $this->createEvent();
117+
$this->configureValidator(true, true);
118+
$this->listener->onTransition($event, 'test_expression');
119+
120+
$this->assertFalse($event->isBlocked());
121+
}
122+
123+
public function testGuardExpressionBlocks()
124+
{
125+
$event = $this->createEvent();
126+
$this->configureValidator(true, false);
127+
$this->listener->onTransition($event, 'test_expression');
128+
129+
$this->assertTrue($event->isBlocked());
130+
}
131+
132+
private function createEvent($newTransition = false)
100133
{
101134
$subject = new \stdClass();
102135
$subject->marking = new Marking();
103-
$transition = new Transition('name', 'from', 'to');
136+
$transition = $this->getTransition($newTransition);
104137

105138
return new GuardEvent($subject, $subject->marking, $transition);
106139
}
@@ -140,4 +173,13 @@ private function configureValidator($isUsed, $valid = true)
140173
->willReturn($valid ? array() : array('a violation'))
141174
;
142175
}
176+
177+
private function getTransition($new = false)
178+
{
179+
if ($new || !$this->transition) {
180+
$this->transition = new Transition('name', 'from', 'to');
181+
}
182+
183+
return $this->transition;
184+
}
143185
}

0 commit comments

Comments
 (0)