[Security] simplified encoder factory implementation

This commit is contained in:
Johannes Schmitt 2011-02-14 17:47:56 +01:00 committed by Fabien Potencier
parent 0643dc44fd
commit 5c7fe8f866
7 changed files with 93 additions and 53 deletions

View File

@ -72,6 +72,10 @@ class SecurityExtension extends Extension
$this->createFirewalls($config, $container);
$this->createAuthorization($config, $container);
$this->createRoleHierarchy($config, $container);
if ($config['encoders']) {
$this->createEncoders($config['encoders'], $container);
}
}
public function aclLoad(array $configs, ContainerBuilder $container)
@ -156,8 +160,6 @@ class SecurityExtension extends Extension
$firewalls = $config['firewalls'];
$providerIds = $this->createUserProviders($config, $container);
$this->createEncoders($config, $container);
// make the ContextListener aware of the configured user providers
$definition = $container->getDefinition('security.context_listener');
$arguments = $definition->getArguments();
@ -329,15 +331,11 @@ class SecurityExtension extends Extension
return array($listeners, $providers, $defaultEntryPoint);
}
protected function createEncoders($config, ContainerBuilder $container)
protected function createEncoders($encoders, ContainerBuilder $container)
{
if (!isset($config['encoders'])) {
return;
}
$encoderMap = array();
foreach ($config['encoders'] as $class => $encoder) {
$encoderMap = $this->createEncoder($encoderMap, $class, $encoder, $container);
foreach ($encoders as $class => $encoder) {
$encoderMap[$class] = $this->createEncoder($class, $encoder, $container);
}
$container
@ -346,16 +344,11 @@ class SecurityExtension extends Extension
;
}
protected function createEncoder(array $encoderMap, $accountClass, $config, ContainerBuilder $container)
protected function createEncoder($accountClass, $config, ContainerBuilder $container)
{
// a custom encoder service
if (isset($config['id'])) {
$container
->getDefinition('security.encoder_factory.generic')
->addMethodCall('addEncoder', array($accountClass, new Reference($config['id'])))
;
return $encoderMap;
return new Reference($config['id']);
}
// plaintext encoder
@ -366,12 +359,10 @@ class SecurityExtension extends Extension
$arguments[0] = $config['ignore_case'];
}
$encoderMap[$accountClass] = array(
return array(
'class' => new Parameter('security.encoder.plain.class'),
'arguments' => $arguments,
);
return $encoderMap;
}
// message digest encoder
@ -390,12 +381,10 @@ class SecurityExtension extends Extension
$arguments[2] = 1;
}
$encoderMap[$accountClass] = array(
return array(
'class' => new Parameter('security.encoder.digest.class'),
'arguments' => $arguments,
);
return $encoderMap;
}
// Parses user providers and returns an array of their ids

View File

@ -1,6 +1,20 @@
<?php
$container->loadFromExtension('security', 'config', array(
'encoders' => array(
'JMS\FooBundle\Entity\User1' => 'plaintext',
'JMS\FooBundle\Entity\User2' => array(
'algorithm' => 'sha1',
'encode_as_base64' => true,
'iterations' => 5,
),
'JMS\FooBundle\Entity\User3' => array(
'algorithm' => 'md5',
),
'JMS\FooBundle\Entity\User4' => array(
'id' => 'security.encoder.foo',
),
),
'providers' => array(
'default' => array(
'users' => array(

View File

@ -6,6 +6,14 @@
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<config>
<encoder class="JMS\FooBundle\Entity\User1" algorithm="plaintext" />
<encoder class="JMS\FooBundle\Entity\User2" algorithm="sha1" encode-as-base64="true" iterations="5" />
<encoder class="JMS\FooBundle\Entity\User3" algorithm="md5" />
<encoder class="JMS\FooBundle\Entity\User4" id="security.encoder.foo" />
<provider name="default">
<user name="foo" password="foo" roles="ROLE_USER" />
</provider>

View File

@ -1,4 +1,15 @@
security.config:
encoders:
JMS\FooBundle\Entity\User1: plaintext
JMS\FooBundle\Entity\User2:
algorithm: sha1
encode_as_base64: true
iterations: 5
JMS\FooBundle\Entity\User3:
algorithm: md5
JMS\FooBundle\Entity\User4:
id: security.encoder.foo
providers:
default:
users:

View File

@ -11,6 +11,10 @@
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -117,6 +121,27 @@ abstract class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
), $container->getParameter('security.role_hierarchy.roles'));
}
public function testEncoders()
{
$container = $this->getContainer('container1');
$this->assertEquals(array(array(
'JMS\FooBundle\Entity\User1' => array(
'class' => new Parameter('security.encoder.plain.class'),
'arguments' => array(),
),
'JMS\FooBundle\Entity\User2' => array(
'class' => new Parameter('security.encoder.digest.class'),
'arguments' => array('sha1', true, 5),
),
'JMS\FooBundle\Entity\User3' => array(
'class' => new Parameter('security.encoder.digest.class'),
'arguments' => array('md5', false, 1),
),
'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'),
)), $container->getDefinition('security.encoder_factory.generic')->getArguments());
}
protected function getContainer($file)
{
$container = new ContainerBuilder();

View File

@ -21,12 +21,10 @@ use Symfony\Component\Security\Core\User\AccountInterface;
class EncoderFactory implements EncoderFactoryInterface
{
protected $encoders;
protected $encoderMap;
public function __construct(array $encoderMap)
public function __construct(array $encoders)
{
$this->encoders = array();
$this->encoderMap = $encoderMap;
$this->encoders = $encoders;
}
/**
@ -35,43 +33,37 @@ class EncoderFactory implements EncoderFactoryInterface
public function getEncoder(AccountInterface $account)
{
foreach ($this->encoders as $class => $encoder) {
if ($account instanceof $class) {
return $encoder;
if (!$account instanceof $class) {
continue;
}
if (!$encoder instanceof PasswordEncoderInterface) {
return $this->encoders[$class] = $this->createEncoder($encoder);
}
return $this->encoders[$class];
}
return $this->createEncoder($account);
}
/**
* Adds an encoder instance to the factory
*
* @param string $class
* @param PasswordEncoderInterface $encoder
* @return void
*/
public function addEncoder($class, PasswordEncoderInterface $encoder)
{
$this->encoders[$class] = $encoder;
throw new \RuntimeException(sprintf('No encoder has been configured for account "%s".', get_class($account)));
}
/**
* Creates the actual encoder instance
*
* @param AccountInterface $account
* @param array $config
* @return PasswordEncoderInterface
*/
protected function createEncoder($account)
protected function createEncoder(array $config)
{
foreach ($this->encoderMap as $class => $config) {
if ($account instanceof $class) {
$reflection = new \ReflectionClass($config['class']);
$this->encoders[$class] = $reflection->newInstanceArgs($config['arguments']);
return $this->encoders[$class];
}
if (!isset($config['class'])) {
throw new \InvalidArgumentException(sprintf('"class" must be set in %s.', json_encode($config)));
}
if (!isset($config['arguments'])) {
throw new \InvalidArgumentException(sprintf('"arguments" must be set in %s.', json_encode($config)));
}
throw new \InvalidArgumentException(sprintf('No encoder has been configured for account "%s".', get_class($account)));
$reflection = new \ReflectionClass($config['class']);
return $reflection->newInstanceArgs($config['arguments']);
}
}

View File

@ -31,8 +31,9 @@ class EncoderFactoryTest extends \PHPUnit_Framework_TestCase
public function testGetEncoderWithService()
{
$factory = new EncoderFactory(array());
$factory->addEncoder('Symfony\Component\Security\Core\User\AccountInterface', new MessageDigestPasswordEncoder('sha1'));
$factory = new EncoderFactory(array(
'Symfony\Component\Security\Core\User\AccountInterface' => new MessageDigestPasswordEncoder('sha1'),
));
$encoder = $factory->getEncoder($this->getMock('Symfony\Component\Security\Core\User\AccountInterface'));
$expectedEncoder = new MessageDigestPasswordEncoder('sha1');