Skip to content

Commit

Permalink
Merge branch '3.3' into 3.4
Browse files Browse the repository at this point in the history
* 3.3:
  better errors when security deps are missing
  • Loading branch information
xabbuh committed Aug 4, 2017
2 parents baa1e7f + f693fcd commit 978eca9
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
@@ -0,0 +1,50 @@
<?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\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;

/**
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
*/
class WorkflowGuardListenerPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasParameter('workflow.has_guard_listeners')) {
return;
}

$container->getParameterBag()->remove('workflow.has_guard_listeners');

if (!$container->has('security.token_storage')) {
throw new LogicException('The "security.token_storage" service is needed to be able to use the workflow guard listener.');
}

if (!$container->has('security.authorization_checker')) {
throw new LogicException('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.');
}

if (!$container->has('security.authentication.trust_resolver')) {
throw new LogicException('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.');
}

if (!$container->has('security.role_hierarchy')) {
throw new LogicException('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.');
}
}
}
Expand Up @@ -52,6 +52,7 @@
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
use Symfony\Component\Routing\Loader\AnnotationFileLoader;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Encoder\CsvEncoder;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
Expand Down Expand Up @@ -624,6 +625,10 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed.');
}

if (!class_exists(Security::class)) {
throw new LogicException('Cannot guard workflows as the Security component is not installed.');
}

$eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName);
$guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
$configuration[$eventName] = $config['guard'];
Expand All @@ -639,6 +644,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
));

$container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard);
$container->setParameter('workflow.has_guard_listeners', true);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -24,6 +24,7 @@
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass;
use Symfony\Component\Config\DependencyInjection\ConfigCachePass;
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
use Symfony\Component\HttpKernel\DependencyInjection\AddCacheClearerPass;
Expand Down Expand Up @@ -111,6 +112,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING);
$this->addCompilerPassIfExists($container, FormPass::class);
$container->addCompilerPass(new WorkflowGuardListenerPass());

if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
Expand Down
@@ -0,0 +1,127 @@
<?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\WorkflowGuardListenerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Workflow\EventListener\GuardListener;

class WorkflowGuardListenerPassTest extends TestCase
{
private $container;
private $compilerPass;

protected function setUp()
{
$this->container = new ContainerBuilder();
$this->container->register('foo.listener.guard', GuardListener::class);
$this->container->register('bar.listener.guard', GuardListener::class);
$this->compilerPass = new WorkflowGuardListenerPass();
}

public function testListenersAreNotRemovedIfParameterIsNotSet()
{
$this->compilerPass->process($this->container);

$this->assertTrue($this->container->hasDefinition('foo.listener.guard'));
$this->assertTrue($this->container->hasDefinition('bar.listener.guard'));
}

public function testParameterIsRemovedWhenThePassIsProcessed()
{
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));

try {
$this->compilerPass->process($this->container);
} catch (LogicException $e) {
// Here, we are not interested in the exception handling. This is tested further down.
}

$this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners'));
}

public function testListenersAreNotRemovedIfAllDependenciesArePresent()
{
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
$this->container->register('security.token_storage', TokenStorageInterface::class);
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
$this->container->register('security.role_hierarchy', RoleHierarchy::class);

$this->compilerPass->process($this->container);

$this->assertTrue($this->container->hasDefinition('foo.listener.guard'));
$this->assertTrue($this->container->hasDefinition('bar.listener.guard'));
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage The "security.token_storage" service is needed to be able to use the workflow guard listener.
*/
public function testListenersAreRemovedIfTheTokenStorageServiceIsNotPresent()
{
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
$this->container->register('security.role_hierarchy', RoleHierarchy::class);

$this->compilerPass->process($this->container);
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage The "security.authorization_checker" service is needed to be able to use the workflow guard listener.
*/
public function testListenersAreRemovedIfTheAuthorizationCheckerServiceIsNotPresent()
{
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
$this->container->register('security.token_storage', TokenStorageInterface::class);
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
$this->container->register('security.role_hierarchy', RoleHierarchy::class);

$this->compilerPass->process($this->container);
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.
*/
public function testListenersAreRemovedIfTheAuthenticationTrustResolverServiceIsNotPresent()
{
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
$this->container->register('security.token_storage', TokenStorageInterface::class);
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
$this->container->register('security.role_hierarchy', RoleHierarchy::class);

$this->compilerPass->process($this->container);
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.
*/
public function testListenersAreRemovedIfTheRoleHierarchyServiceIsNotPresent()
{
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
$this->container->register('security.token_storage', TokenStorageInterface::class);
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);

$this->compilerPass->process($this->container);
}
}

0 comments on commit 978eca9

Please sign in to comment.