[FrameworkBundle] fixed guard event names for transitions
This commit is contained in:
parent
fb88bfc79a
commit
83dc473dd6
@ -602,15 +602,44 @@ class FrameworkExtension extends Extension
|
|||||||
@trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED);
|
@trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED);
|
||||||
}
|
}
|
||||||
$type = $workflow['type'];
|
$type = $workflow['type'];
|
||||||
|
$workflowId = sprintf('%s.%s', $type, $name);
|
||||||
|
|
||||||
|
// Create transitions
|
||||||
$transitions = array();
|
$transitions = array();
|
||||||
|
$guardsConfiguration = array();
|
||||||
|
// Global transition counter per workflow
|
||||||
|
$transitionCounter = 0;
|
||||||
foreach ($workflow['transitions'] as $transition) {
|
foreach ($workflow['transitions'] as $transition) {
|
||||||
if ('workflow' === $type) {
|
if ('workflow' === $type) {
|
||||||
$transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $transition['from'], $transition['to']));
|
$transitionDefinition = new Definition(Workflow\Transition::class, array($transition['name'], $transition['from'], $transition['to']));
|
||||||
|
$transitionDefinition->setPublic(false);
|
||||||
|
$transitionId = sprintf('%s.transition.%s', $workflowId, $transitionCounter++);
|
||||||
|
$container->setDefinition($transitionId, $transitionDefinition);
|
||||||
|
$transitions[] = new Reference($transitionId);
|
||||||
|
if (isset($transition['guard'])) {
|
||||||
|
$configuration = new Definition(Workflow\EventListener\GuardExpression::class);
|
||||||
|
$configuration->addArgument(new Reference($transitionId));
|
||||||
|
$configuration->addArgument($transition['guard']);
|
||||||
|
$configuration->setPublic(false);
|
||||||
|
$eventName = sprintf('workflow.%s.guard.%s', $name, $transition['name']);
|
||||||
|
$guardsConfiguration[$eventName][] = $configuration;
|
||||||
|
}
|
||||||
} elseif ('state_machine' === $type) {
|
} elseif ('state_machine' === $type) {
|
||||||
foreach ($transition['from'] as $from) {
|
foreach ($transition['from'] as $from) {
|
||||||
foreach ($transition['to'] as $to) {
|
foreach ($transition['to'] as $to) {
|
||||||
$transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $from, $to));
|
$transitionDefinition = new Definition(Workflow\Transition::class, array($transition['name'], $from, $to));
|
||||||
|
$transitionDefinition->setPublic(false);
|
||||||
|
$transitionId = sprintf('%s.transition.%s', $workflowId, $transitionCounter++);
|
||||||
|
$container->setDefinition($transitionId, $transitionDefinition);
|
||||||
|
$transitions[] = new Reference($transitionId);
|
||||||
|
if (isset($transition['guard'])) {
|
||||||
|
$configuration = new Definition(Workflow\EventListener\GuardExpression::class);
|
||||||
|
$configuration->addArgument(new Reference($transitionId));
|
||||||
|
$configuration->addArgument($transition['guard']);
|
||||||
|
$configuration->setPublic(false);
|
||||||
|
$eventName = sprintf('workflow.%s.guard.%s', $name, $transition['name']);
|
||||||
|
$guardsConfiguration[$eventName][] = $configuration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -641,7 +670,6 @@ class FrameworkExtension extends Extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create Workflow
|
// Create Workflow
|
||||||
$workflowId = sprintf('%s.%s', $type, $name);
|
|
||||||
$workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type));
|
$workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type));
|
||||||
$workflowDefinition->replaceArgument(0, new Reference(sprintf('%s.definition', $workflowId)));
|
$workflowDefinition->replaceArgument(0, new Reference(sprintf('%s.definition', $workflowId)));
|
||||||
if (isset($markingStoreDefinition)) {
|
if (isset($markingStoreDefinition)) {
|
||||||
@ -677,16 +705,7 @@ class FrameworkExtension extends Extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add Guard Listener
|
// Add Guard Listener
|
||||||
$guard = new Definition(Workflow\EventListener\GuardListener::class);
|
if ($guardsConfiguration) {
|
||||||
$guard->setPrivate(true);
|
|
||||||
$configuration = array();
|
|
||||||
foreach ($workflow['transitions'] as $config) {
|
|
||||||
$transitionName = $config['name'];
|
|
||||||
|
|
||||||
if (!isset($config['guard'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!class_exists(ExpressionLanguage::class)) {
|
if (!class_exists(ExpressionLanguage::class)) {
|
||||||
throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".');
|
throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".');
|
||||||
}
|
}
|
||||||
@ -695,13 +714,11 @@ class FrameworkExtension extends Extension
|
|||||||
throw new LogicException('Cannot guard workflows as the Security component is not installed. Try running "composer require symfony/security".');
|
throw new LogicException('Cannot guard workflows as the Security component is not installed. Try running "composer require symfony/security".');
|
||||||
}
|
}
|
||||||
|
|
||||||
$eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName);
|
$guard = new Definition(Workflow\EventListener\GuardListener::class);
|
||||||
$guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
|
$guard->setPrivate(true);
|
||||||
$configuration[$eventName] = $config['guard'];
|
|
||||||
}
|
|
||||||
if ($configuration) {
|
|
||||||
$guard->setArguments(array(
|
$guard->setArguments(array(
|
||||||
$configuration,
|
$guardsConfiguration,
|
||||||
new Reference('workflow.security.expression_language'),
|
new Reference('workflow.security.expression_language'),
|
||||||
new Reference('security.token_storage'),
|
new Reference('security.token_storage'),
|
||||||
new Reference('security.authorization_checker'),
|
new Reference('security.authorization_checker'),
|
||||||
@ -709,6 +726,9 @@ class FrameworkExtension extends Extension
|
|||||||
new Reference('security.role_hierarchy'),
|
new Reference('security.role_hierarchy'),
|
||||||
new Reference('validator', ContainerInterface::NULL_ON_INVALID_REFERENCE),
|
new Reference('validator', ContainerInterface::NULL_ON_INVALID_REFERENCE),
|
||||||
));
|
));
|
||||||
|
foreach ($guardsConfiguration as $eventName => $config) {
|
||||||
|
$guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
|
||||||
|
}
|
||||||
|
|
||||||
$container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard);
|
$container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard);
|
||||||
$container->setParameter('workflow.has_guard_listeners', true);
|
$container->setParameter('workflow.has_guard_listeners', true);
|
||||||
|
@ -13,12 +13,12 @@
|
|||||||
<framework:argument>a</framework:argument>
|
<framework:argument>a</framework:argument>
|
||||||
</framework:marking-store>
|
</framework:marking-store>
|
||||||
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
|
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest</framework:support>
|
||||||
<framework:place name="draft" />
|
<framework:place>draft</framework:place>
|
||||||
<framework:place name="wait_for_journalist" />
|
<framework:place>wait_for_journalist</framework:place>
|
||||||
<framework:place name="approved_by_journalist" />
|
<framework:place>approved_by_journalist</framework:place>
|
||||||
<framework:place name="wait_for_spellchecker" />
|
<framework:place>wait_for_spellchecker</framework:place>
|
||||||
<framework:place name="approved_by_spellchecker" />
|
<framework:place>approved_by_spellchecker</framework:place>
|
||||||
<framework:place name="published" />
|
<framework:place>published</framework:place>
|
||||||
<framework:transition name="request_review">
|
<framework:transition name="request_review">
|
||||||
<framework:from>draft</framework:from>
|
<framework:from>draft</framework:from>
|
||||||
<framework:to>wait_for_journalist</framework:to>
|
<framework:to>wait_for_journalist</framework:to>
|
||||||
|
@ -19,19 +19,22 @@ class GuardExpression
|
|||||||
|
|
||||||
private $expression;
|
private $expression;
|
||||||
|
|
||||||
public function getTransition(): Transition
|
/**
|
||||||
{
|
* @param string $expression
|
||||||
return $this->transition;
|
*/
|
||||||
}
|
public function __construct(Transition $transition, $expression)
|
||||||
|
|
||||||
public function getExpression(): string
|
|
||||||
{
|
|
||||||
return $this->expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct(Transition $transition, string $expression)
|
|
||||||
{
|
{
|
||||||
$this->transition = $transition;
|
$this->transition = $transition;
|
||||||
$this->expression = $expression;
|
$this->expression = $expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTransition()
|
||||||
|
{
|
||||||
|
return $this->transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExpression()
|
||||||
|
{
|
||||||
|
return $this->expression;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
|
|||||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||||
use Symfony\Component\Workflow\Exception\InvalidTokenConfigurationException;
|
use Symfony\Component\Workflow\Exception\InvalidTokenConfigurationException;
|
||||||
use Symfony\Component\Workflow\TransitionBlocker;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||||
@ -63,16 +62,15 @@ class GuardListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validateGuardExpression(GuardEvent $event, string $expression)
|
private function validateGuardExpression(GuardEvent $event, $expression)
|
||||||
{
|
{
|
||||||
if (!$this->expressionLanguage->evaluate($expression, $this->getVariables($event))) {
|
if (!$this->expressionLanguage->evaluate($expression, $this->getVariables($event))) {
|
||||||
$blocker = TransitionBlocker::createBlockedByExpressionGuardListener($expression);
|
$event->setBlocked(true);
|
||||||
$event->addTransitionBlocker($blocker);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
|
// code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter
|
||||||
private function getVariables(GuardEvent $event): array
|
private function getVariables(GuardEvent $event)
|
||||||
{
|
{
|
||||||
$token = $this->tokenStorage->getToken();
|
$token = $this->tokenStorage->getToken();
|
||||||
|
|
||||||
|
@ -21,16 +21,16 @@ class GuardListenerTest extends TestCase
|
|||||||
private $authenticationChecker;
|
private $authenticationChecker;
|
||||||
private $validator;
|
private $validator;
|
||||||
private $listener;
|
private $listener;
|
||||||
private $transition;
|
private $configuration;
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$configuration = array(
|
$this->configuration = array(
|
||||||
'test_is_granted' => 'is_granted("something")',
|
'test_is_granted' => 'is_granted("something")',
|
||||||
'test_is_valid' => 'is_valid(subject)',
|
'test_is_valid' => 'is_valid(subject)',
|
||||||
'test_expression' => array(
|
'test_expression' => array(
|
||||||
new GuardExpression($this->getTransition(true), '!is_valid(subject)'),
|
new GuardExpression(new Transition('name', 'from', 'to'), '!is_valid(subject)'),
|
||||||
new GuardExpression($this->getTransition(true), 'is_valid(subject)'),
|
new GuardExpression(new Transition('name', 'from', 'to'), 'is_valid(subject)'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
$expressionLanguage = new ExpressionLanguage();
|
$expressionLanguage = new ExpressionLanguage();
|
||||||
@ -41,7 +41,7 @@ class GuardListenerTest extends TestCase
|
|||||||
$this->authenticationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
|
$this->authenticationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
|
||||||
$trustResolver = $this->getMockBuilder(AuthenticationTrustResolverInterface::class)->getMock();
|
$trustResolver = $this->getMockBuilder(AuthenticationTrustResolverInterface::class)->getMock();
|
||||||
$this->validator = $this->getMockBuilder(ValidatorInterface::class)->getMock();
|
$this->validator = $this->getMockBuilder(ValidatorInterface::class)->getMock();
|
||||||
$this->listener = new GuardListener($configuration, $expressionLanguage, $tokenStorage, $this->authenticationChecker, $trustResolver, null, $this->validator);
|
$this->listener = new GuardListener($this->configuration, $expressionLanguage, $tokenStorage, $this->authenticationChecker, $trustResolver, null, $this->validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
@ -104,8 +104,8 @@ class GuardListenerTest extends TestCase
|
|||||||
|
|
||||||
public function testWithGuardExpressionWithNotSupportedTransition()
|
public function testWithGuardExpressionWithNotSupportedTransition()
|
||||||
{
|
{
|
||||||
$event = $this->createEvent(true);
|
$event = $this->createEvent();
|
||||||
$this->configureValidator(false, false);
|
$this->configureValidator(false);
|
||||||
$this->listener->onTransition($event, 'test_expression');
|
$this->listener->onTransition($event, 'test_expression');
|
||||||
|
|
||||||
$this->assertFalse($event->isBlocked());
|
$this->assertFalse($event->isBlocked());
|
||||||
@ -113,7 +113,7 @@ class GuardListenerTest extends TestCase
|
|||||||
|
|
||||||
public function testWithGuardExpressionWithSupportedTransition()
|
public function testWithGuardExpressionWithSupportedTransition()
|
||||||
{
|
{
|
||||||
$event = $this->createEvent();
|
$event = $this->createEvent($this->configuration['test_expression'][1]->getTransition());
|
||||||
$this->configureValidator(true, true);
|
$this->configureValidator(true, true);
|
||||||
$this->listener->onTransition($event, 'test_expression');
|
$this->listener->onTransition($event, 'test_expression');
|
||||||
|
|
||||||
@ -122,18 +122,18 @@ class GuardListenerTest extends TestCase
|
|||||||
|
|
||||||
public function testGuardExpressionBlocks()
|
public function testGuardExpressionBlocks()
|
||||||
{
|
{
|
||||||
$event = $this->createEvent();
|
$event = $this->createEvent($this->configuration['test_expression'][1]->getTransition());
|
||||||
$this->configureValidator(true, false);
|
$this->configureValidator(true, false);
|
||||||
$this->listener->onTransition($event, 'test_expression');
|
$this->listener->onTransition($event, 'test_expression');
|
||||||
|
|
||||||
$this->assertTrue($event->isBlocked());
|
$this->assertTrue($event->isBlocked());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createEvent($newTransition = false)
|
private function createEvent(Transition $transition = null)
|
||||||
{
|
{
|
||||||
$subject = new \stdClass();
|
$subject = new \stdClass();
|
||||||
$subject->marking = new Marking();
|
$subject->marking = new Marking();
|
||||||
$transition = $this->getTransition($newTransition);
|
$transition = $transition ?: new Transition('name', 'from', 'to');
|
||||||
|
|
||||||
return new GuardEvent($subject, $subject->marking, $transition);
|
return new GuardEvent($subject, $subject->marking, $transition);
|
||||||
}
|
}
|
||||||
@ -173,13 +173,4 @@ class GuardListenerTest extends TestCase
|
|||||||
->willReturn($valid ? array() : array('a violation'))
|
->willReturn($valid ? array() : array('a violation'))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getTransition($new = false)
|
|
||||||
{
|
|
||||||
if ($new || !$this->transition) {
|
|
||||||
$this->transition = new Transition('name', 'from', 'to');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->transition;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user