From 82c684414757af8951cef3bf0ec1b36d98f62aba Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 16 Feb 2011 02:53:32 +0100 Subject: [PATCH] [Security] moved Security classes out of DoctrineBundle, cleaned-up SecurityExtension accordingly Note that this commit removes the built-in support for MongoDB user providers. This code can be moved back in once there is a stable release for MongoDB, but for now you have to set-up that user provider just like you would set-up any custom user provider: security: providers: document_provider: id: my.mongo.provider --- .../DoctrineBundle/Resources/config/orm.xml | 8 --- .../DependencyInjection/Configuration.php | 23 +++---- .../DependencyInjection/SecurityExtension.php | 68 +++++++------------ .../Resources/config/security.xml | 12 ++++ .../Resources/config/security_acl.xml | 26 +++---- .../SecurityExtensionTest.php | 20 +++--- .../Acl/Domain/AclCollectionCache.php | 66 ++++++++++++++++++ .../Core/User}/EntityUserProvider.php | 15 ++-- ...namePasswordFormAuthenticationListener.php | 2 +- 9 files changed, 148 insertions(+), 92 deletions(-) create mode 100644 src/Symfony/Component/Security/Acl/Domain/AclCollectionCache.php rename src/Symfony/{Bundle/DoctrineBundle/Security => Component/Security/Core/User}/EntityUserProvider.php (83%) diff --git a/src/Symfony/Bundle/DoctrineBundle/Resources/config/orm.xml b/src/Symfony/Bundle/DoctrineBundle/Resources/config/orm.xml index e5df6c8b8151..7c0ef382ea8a 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Resources/config/orm.xml +++ b/src/Symfony/Bundle/DoctrineBundle/Resources/config/orm.xml @@ -37,12 +37,6 @@ Doctrine\ORM\Mapping\Driver\StaticPHPDriver Doctrine\ORM\Mapping\ClassMetadataFactory - - Symfony\Bundle\DoctrineBundle\Security\EntityUserProvider - - - Symfony\Bundle\DoctrineBundle\Security\AclCollectionCache - Symfony\Bundle\DoctrineBundle\CacheWarmer\ProxyCacheWarmer @@ -59,8 +53,6 @@ - - diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php index 9df9589e40ad..5acf264f3a5e 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php @@ -17,18 +17,6 @@ */ class Configuration { - public function getAclConfigTree() - { - $tb = new TreeBuilder(); - - return $tb - ->root('security', 'array') - ->scalarNode('connection')->end() - ->scalarNode('cache')->end() - ->end() - ->buildTree(); - } - public function getFactoryConfigTree() { $tb = new TreeBuilder(); @@ -53,6 +41,7 @@ public function getMainConfigTree(array $factories) ->scalarNode('session_fixation_strategy')->cannotBeEmpty()->defaultValue('migrate')->end() ; + $this->addAclSection($rootNode); $this->addEncodersSection($rootNode); $this->addProvidersSection($rootNode); $this->addFirewallsSection($rootNode, $factories); @@ -62,6 +51,16 @@ public function getMainConfigTree(array $factories) return $tb->buildTree(); } + protected function addAclSection($rootNode) + { + $rootNode + ->arrayNode('acl') + ->scalarNode('connection')->end() + ->scalarNode('cache')->end() + ->end() + ; + } + protected function addRoleHierarchySection($rootNode) { $rootNode diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 29a0bd9f9816..abfd2fd8d601 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -47,8 +47,6 @@ public function __construct() public function load(array $configs, ContainerBuilder $container) { - $this->aclLoad($configs, $container); - $tmp = array_filter($configs); if (empty($tmp)) { return; @@ -83,22 +81,10 @@ public function load(array $configs, ContainerBuilder $container) if ($config['encoders']) { $this->createEncoders($config['encoders'], $container); } - } - - protected function aclLoad(array $configs, ContainerBuilder $container) - { - $processor = new Processor(); - $config = $processor->process($this->configuration->getAclConfigTree(), $configs); - - $loader = new XmlFileLoader($container, new FileLocator(array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config'))); - $loader->load('security_acl.xml'); - - if (isset($config['connection'])) { - $container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection'])); - } - if (isset($config['cache'])) { - $container->setAlias('security.acl.cache', sprintf('security.acl.cache.%s', $config['cache'])); + // load ACL + if (isset($config['acl'])) { + $this->aclLoad($config['acl'], $container); } } @@ -122,6 +108,20 @@ public function getAlias() return 'security'; } + protected function aclLoad($config, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config'))); + $loader->load('security_acl.xml'); + + if (isset($config['connection'])) { + $container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection'])); + } + + if (isset($config['cache'])) { + $container->setAlias('security.acl.cache', sprintf('security.acl.cache.%s', $config['cache'])); + } + } + /** * Loads the web configuration. * @@ -421,8 +421,6 @@ protected function createUserProviders($config, ContainerBuilder $container) } // Parses a tag and returns the id for the related user provider service - // FIXME: Replace register() calls in this method with DefinitionDecorator - // and move the actual definition to an xml file protected function createUserDaoProvider($name, $provider, ContainerBuilder $container, $master = true) { $name = $this->getUserProviderId(strtolower($name)); @@ -443,42 +441,22 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con // Doctrine Entity DAO provider if (isset($provider['entity'])) { $container - ->register($name, '%security.user.provider.entity.class%') - ->setPublic(false) - ->setArguments(array( - new Reference('security.user.entity_manager'), - $provider['entity']['class'], - $provider['entity']['property'], - )) + ->setDefinition($name, new DefinitionDecorator('security.user.provider.entity')) + ->addArgument($provider['entity']['class']) + ->addArgument($provider['entity']['property']) ; return $name; } - // Doctrine Document DAO provider - if (isset($provider['document'])) { - $container - ->register($name, '%security.user.provider.document.class%') - ->setPublic(false) - ->setArguments(array( - new Reference('security.user.document_manager'), - $provider['document']['class'], - $provider['document']['property'], - )); - - return $name; - } - // In-memory DAO provider - $definition = $container->register($name, '%security.user.provider.in_memory.class%'); - $definition->setPublic(false); + $definition = $container->setDefinition($name, new DefinitionDecorator('security.user.provider.in_memory')); foreach ($provider['users'] as $username => $user) { $userId = $name.'_'.$username; $container - ->register($userId, 'Symfony\Component\Security\Core\User\User') + ->setDefinition($userId, new DefinitionDecorator('security.user.provider.in_memory.user')) ->setArguments(array($username, $user['password'], $user['roles'])) - ->setPublic(false) ; $definition->addMethodCall('createUser', array(new Reference($userId))); @@ -489,7 +467,7 @@ protected function createUserDaoProvider($name, $provider, ContainerBuilder $con protected function getUserProviderId($name) { - return 'security.user.provider.'.$name; + return 'security.user.provider.concrete.'.$name; } protected function createExceptionListener($container, $config, $id, $defaultEntryPoint) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index bdab6f915196..8e53998834f2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -14,7 +14,9 @@ Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder + Symfony\Component\Security\Core\User\EntityUserProvider Symfony\Component\Security\Core\User\InMemoryUserProvider + Symfony\Component\Security\Core\User\User Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver Symfony\Component\Security\Core\Authentication\Token\AnonymousToken @@ -117,5 +119,15 @@ + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml index 6e293cc6a258..fb51ffe1b8f1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml @@ -11,30 +11,32 @@ acl_object_identity_ancestors acl_security_identities Symfony\Component\Security\Acl\Dbal\MutableAclProvider - + Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy - + Symfony\Component\Security\Acl\Voter\AclVoter true Symfony\Component\Security\Acl\Permission\BasicPermissionMap - + Symfony\Component\Security\Acl\Domain\ObjectIdentityRetrievalStrategy Symfony\Component\Security\Acl\Domain\SecurityIdentityRetrievalStrategy - + Symfony\Component\Security\Acl\Domain\DoctrineAclCache sf2_acl_ + + Symfony\Component\Security\Acl\Domain\AclCollectionCache - + - + - + @@ -49,23 +51,23 @@ - + - + %security.acl.cache.doctrine.prefix% - + - + - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 8332d86c4281..0a57f06a7767 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -36,18 +36,18 @@ public function testUserProviders() { $container = $this->getContainer('container1'); - $providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.user.provider.'); })); + $providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.user.provider.concrete'); })); $expectedProviders = array( - 'security.user.provider.default', - 'security.user.provider.default_foo', - 'security.user.provider.digest', - 'security.user.provider.digest_foo', - 'security.user.provider.basic', - 'security.user.provider.basic_foo', - 'security.user.provider.basic_bar', - 'security.user.provider.doctrine', - 'security.user.provider.service', + 'security.user.provider.concrete.default', + 'security.user.provider.concrete.default_foo', + 'security.user.provider.concrete.digest', + 'security.user.provider.concrete.digest_foo', + 'security.user.provider.concrete.basic', + 'security.user.provider.concrete.basic_foo', + 'security.user.provider.concrete.basic_bar', + 'security.user.provider.concrete.doctrine', + 'security.user.provider.concrete.service', ); $this->assertEquals(array(), array_diff($expectedProviders, $providers)); diff --git a/src/Symfony/Component/Security/Acl/Domain/AclCollectionCache.php b/src/Symfony/Component/Security/Acl/Domain/AclCollectionCache.php new file mode 100644 index 000000000000..5ac8dfafe577 --- /dev/null +++ b/src/Symfony/Component/Security/Acl/Domain/AclCollectionCache.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Acl\Domain; + +use Symfony\Component\Security\Acl\Model\AclProviderInterface; +use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; +use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface; + +/** + * This service caches ACLs for an entire collection of objects. + * + * @author Johannes M. Schmitt + */ +class AclCollectionCache +{ + protected $aclProvider; + protected $objectIdentityRetrievalStrategy; + protected $securityIdentityRetrievalStrategy; + + /** + * Constructor + * + * @param AclProviderInterface $aclProvider + * @param ObjectIdentityRetrievalStrategy $oidRetrievalStrategy + * @param SecurityIdentityRetrievalStrategy $sidRetrievalStrategy + * @return void + */ + public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy) + { + $this->aclProvider = $aclProvider; + $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy; + $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy; + } + + /** + * Batch loads ACLs for an entire collection; thus, it reduces the number + * of required queries considerably. + * + * @param mixed $collection anything that can be passed to foreach() + * @param array $tokens an array of TokenInterface implementations + * @return void + */ + public function cache($collection, array $tokens = array()) + { + $sids = array(); + foreach ($tokens as $token) { + $sids = array_merge($sids, $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token)); + } + + $oids = array(); + foreach ($collection as $domainObject) { + $oids[] = $this->objectIdentityRetrievalStrategy->getObjectIdentity($domainObject); + } + + $this->aclProvider->findAcls($oids, $sids); + } +} \ No newline at end of file diff --git a/src/Symfony/Bundle/DoctrineBundle/Security/EntityUserProvider.php b/src/Symfony/Component/Security/Core/User/EntityUserProvider.php similarity index 83% rename from src/Symfony/Bundle/DoctrineBundle/Security/EntityUserProvider.php rename to src/Symfony/Component/Security/Core/User/EntityUserProvider.php index 03094c29ca29..89ff443448f5 100644 --- a/src/Symfony/Bundle/DoctrineBundle/Security/EntityUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/EntityUserProvider.php @@ -9,20 +9,27 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\DoctrineBundle\Security; +namespace Symfony\Component\Security\Core\User; -use Symfony\Component\Security\Core\User\AccountInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; +use Doctrine\ORM\EntityManager; use Symfony\Component\Security\Core\Exception\UnsupportedAccountException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +/** + * Wrapper around a Doctrine EntityManager. + * + * Provides easy to use provisioning for Doctrine entity users. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ class EntityUserProvider implements UserProviderInterface { protected $class; protected $repository; protected $property; - public function __construct($em, $class, $property = null) + public function __construct(EntityManager $em, $class, $property = null) { $this->class = $class; diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index 0e3b396b0f12..d7518e6f7d7e 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -64,7 +64,7 @@ protected function attemptAuthentication(Request $request) if (null !== $this->csrfProvider) { $csrfToken = $request->get($this->options['csrf_parameter']); - if (false === $this->csrfProvider->isTokenValid($this->options['csrf_page_id'], $csrfToken)) { + if (false === $this->csrfProvider->isCsrfTokenValid($this->options['csrf_page_id'], $csrfToken)) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } }