فهرست منبع

added generic encoder factory

Johannes M. Schmitt 14 سال پیش
والد
کامیت
314defa8b4

+ 2 - 2
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/FormLoginFactory.php

@@ -21,12 +21,12 @@ use Symfony\Component\DependencyInjection\Reference;
  */
 class FormLoginFactory implements SecurityFactoryInterface
 {
-    public function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint)
+    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
     {
         $provider = 'security.authentication.provider.dao.'.$id;
         $container
             ->register($provider, '%security.authentication.provider.dao.class%')
-            ->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder.'.$providerIds[$userProvider])))
+            ->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder_factory')))
             ->setPublic(false)
         ;
 

+ 2 - 2
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php

@@ -21,12 +21,12 @@ use Symfony\Component\DependencyInjection\Reference;
  */
 class HttpBasicFactory implements SecurityFactoryInterface
 {
-    public function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint)
+    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
     {
         $provider = 'security.authentication.provider.dao.'.$id;
         $container
             ->register($provider, '%security.authentication.provider.dao.class%')
-            ->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder.'.$providerIds[$userProvider])))
+            ->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder_factory')))
             ->setPublic(false)
         ;
 

+ 2 - 2
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php

@@ -21,12 +21,12 @@ use Symfony\Component\DependencyInjection\Reference;
  */
 class HttpDigestFactory implements SecurityFactoryInterface
 {
-    public function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint)
+    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
     {
         $provider = 'security.authentication.provider.dao.'.$id;
         $container
             ->register($provider, '%security.authentication.provider.dao.class%')
-            ->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder.'.$providerIds[$userProvider])))
+            ->setArguments(array(new Reference($userProvider), new Reference('security.account_checker'), new Reference('security.encoder_factory')))
             ->setPublic(false)
         ;
 

+ 1 - 1
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php

@@ -20,7 +20,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
  */
 interface SecurityFactoryInterface
 {
-    function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint);
+    function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint);
 
     function getPosition();
 

+ 1 - 1
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Security/Factory/X509Factory.php

@@ -21,7 +21,7 @@ use Symfony\Component\DependencyInjection\Reference;
  */
 class X509Factory implements SecurityFactoryInterface
 {
-    public function create(ContainerBuilder $container, $id, $config, $userProvider, $providerIds, $defaultEntryPoint)
+    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
     {
         $provider = 'security.authentication.provider.pre_authenticated.'.$id;
         $container

+ 107 - 24
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/SecurityExtension.php

@@ -130,6 +130,8 @@ class SecurityExtension extends Extension
     {
         $providerIds = $this->createUserProviders($config, $container);
 
+        $encoders = $this->createEncoders($config, $container);
+
         if (!$firewalls = $this->fixConfig($config, 'firewall')) {
             return;
         }
@@ -138,7 +140,7 @@ class SecurityExtension extends Extension
         $definition = $container->getDefinition('security.context_listener');
         $arguments = $definition->getArguments();
         $userProviders = array();
-        foreach (array_keys($providerIds) as $userProviderId) {
+        foreach ($providerIds as $userProviderId) {
             $userProviders[] = new Reference($userProviderId);
         }
         $arguments[1] = $userProviders;
@@ -193,8 +195,7 @@ class SecurityExtension extends Extension
             if (!$providerIds) {
                 throw new \InvalidArgumentException('You must provide at least one authentication provider.');
             }
-            $keys = array_keys($providerIds);
-            $defaultProvider = current($keys);
+            $defaultProvider = reset($providerIds);
         }
 
         // Register listeners
@@ -240,7 +241,7 @@ class SecurityExtension extends Extension
         }
 
         // Authentication listeners
-        list($authListeners, $providers, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $defaultProvider, $providerIds);
+        list($authListeners, $providers, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $defaultProvider);
 
         $listeners = array_merge($listeners, $authListeners);
 
@@ -261,7 +262,7 @@ class SecurityExtension extends Extension
         return array($matcher, $listeners, $exceptionListener);
     }
 
-    protected function createAuthenticationListeners($container, $id, $firewall, $defaultProvider, $providerIds)
+    protected function createAuthenticationListeners($container, $id, $firewall, $defaultProvider)
     {
         $listeners = array();
         $providers = array();
@@ -293,7 +294,7 @@ class SecurityExtension extends Extension
                 if (array_key_exists($key, $firewall)) {
                     $userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider;
 
-                    list($provider, $listener, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $providerIds, $defaultEntryPoint);
+                    list($provider, $listener, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
 
                     $listeners[] = new Reference($listener);
                     $providers[] = new Reference($provider);
@@ -325,29 +326,111 @@ class SecurityExtension extends Extension
 
         $providerIds = array();
         foreach ($providers as $name => $provider) {
-            list($id, $encoder) = $this->createUserDaoProvider($name, $provider, $container);
+            $id = $this->createUserDaoProvider($name, $provider, $container);
 
-            if (isset($providerIds[$id])) {
+            if (in_array($id, $providerIds, true)) {
                 throw new \RuntimeException(sprintf('Provider names must be unique. Duplicate entry for %s.', $id));
             }
 
-            $providerIds[$id] = $encoder;
+            $providerIds[] = $id;
         }
 
         return $providerIds;
     }
 
-    // Parses a <provider> tag and returns the id for the related user provider service
-    protected function createUserDaoProvider($name, $provider, ContainerBuilder $container, $master = true)
+    protected function createEncoders($config, ContainerBuilder $container)
     {
-        // encoder
-        $encoder = 'plain';
-        if (isset($provider['password-encoder'])) {
-            $encoder = $provider['password-encoder'];
-        } elseif (isset($provider['password_encoder'])) {
-            $encoder = $provider['password_encoder'];
+        $encoders = $this->fixConfig($config, 'encoder');
+        if (!$encoders) {
+            return array();
+        }
+
+        $encoderMap = array();
+        foreach ($encoders as $class => $encoder) {
+            $encoderMap = $this->createEncoder($encoderMap, $class, $encoder, $container);
+        }
+
+        $container
+            ->getDefinition('security.encoder_factory.generic')
+            ->setArguments(array($encoderMap))
+        ;
+    }
+
+    protected function createEncoder(array $encoderMap, $accountClass, $config, ContainerBuilder $container)
+    {
+        if (is_array($config) && isset($config['class'])) {
+            $accountClass = $config['class'];
+        }
+
+        if (empty($accountClass)) {
+            throw new \RuntimeException('Each encoder needs an account class.');
+        }
+
+        // a minimal message digest, or plaintext encoder
+        if (is_string($config)) {
+            $config = array(
+                'algorithm' => $config,
+            );
+        }
+
+        // a custom encoder service
+        if (isset($config['id'])) {
+            $container
+                ->getDefinition('security.encoder_factory.generic')
+                ->addMethodCall('addEncoder', array($accountClass, new Reference($config['id'])))
+            ;
+
+            return $encoderMap;
+        }
+
+        // a lazy loaded, message digest or plaintext encoder
+        if (!isset($config['algorithm'])) {
+            throw new \RuntimeException('"algoritm" must be defined.');
+        }
+
+        // plaintext encoder
+        if ('plaintext' === $config['algorithm']) {
+            $arguments = array();
+
+            if (array_key_exists('ignore-case', $config)) {
+                $arguments[0] = (Boolean) $config['ignore-case'];
+            }
+
+            $encoderMap[$accountClass] = array(
+                'class' => new Parameter('security.encoder.plain.class'),
+                'arguments' => $arguments,
+            );
+
+            return $encoderMap;
+        }
+
+        // message digest encoder
+        $arguments = array($config['algorithm']);
+
+        // add optional arguments
+        if (isset($config['encode-as-base64'])) {
+            $arguments[1] = (Boolean) $config['encode-as-base64'];
+        } else {
+            $arguments[1] = false;
         }
 
+        if (isset($config['iterations'])) {
+            $arguments[2] = $config['iterations'];
+        } else {
+            $arguments[2] = 1;
+        }
+
+        $encoderMap[$accountClass] = array(
+            'class' => new Parameter('security.encoder.digest.class'),
+            'arguments' => $arguments,
+        );
+
+        return $encoderMap;
+    }
+
+    // Parses a <provider> tag and returns the id for the related user provider service
+    protected function createUserDaoProvider($name, $provider, ContainerBuilder $container, $master = true)
+    {
         if (isset($provider['name'])) {
             $name = $provider['name'];
         }
@@ -362,7 +445,7 @@ class SecurityExtension extends Extension
         if (isset($provider['id'])) {
             $container->setAlias($name, $provider['id']);
 
-            return array($name, $encoder);
+            return $provider['id'];
         }
 
         // Chain provider
@@ -382,7 +465,7 @@ class SecurityExtension extends Extension
                     isset($provider['entity']['property']) ? $provider['entity']['property'] : null,
             ));
 
-            return array($name, $encoder);
+            return $name;
         }
 
         // Doctrine Document DAO provider
@@ -396,7 +479,7 @@ class SecurityExtension extends Extension
                     isset($provider['document']['property']) ? $provider['document']['property'] : null,
             ));
 
-            return array($name, $encoder);
+            return $name;
         }
 
         // In-memory DAO provider
@@ -433,7 +516,7 @@ class SecurityExtension extends Extension
             $definition->addMethodCall('createUser', array(new Reference($userId)));
         }
 
-        return array($name, $encoder);
+        return $name;
     }
 
     protected function getUserProviderId($name)
@@ -492,18 +575,18 @@ class SecurityExtension extends Extension
 
         return $switchUserListenerId;
     }
-    
+
     public function aclLoad(array $config, ContainerBuilder $container)
     {
         if (!$container->hasDefinition('security.acl')) {
             $loader = new XmlFileLoader($container, array(__DIR__.'/../Resources/config', __DIR__.'/Resources/config'));
             $loader->load('security_acl.xml');
         }
-        
+
         if (isset($config['connection'])) {
             $container->setAlias(sprintf('doctrine.dbal.%s_connection', $config['connection']), 'security.acl.dbal.connection');
         }
-        
+
         if (isset($config['cache'])) {
             $container->setAlias('security.acl.cache', sprintf('security.acl.cache.%s', $config['cache']));
         } else {

+ 5 - 8
src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml

@@ -11,6 +11,7 @@
 
         <parameter key="security.access_denied.url">null</parameter>
 
+        <parameter key="security.encoder_factory.generic.class">Symfony\Component\Security\Encoder\EncoderFactory</parameter>
         <parameter key="security.encoder.digest.class">Symfony\Component\Security\Encoder\MessageDigestPasswordEncoder</parameter>
         <parameter key="security.encoder.plain.class">Symfony\Component\Security\Encoder\PlaintextPasswordEncoder</parameter>
 
@@ -91,15 +92,11 @@
 
         <service id="security.account_checker" class="%security.account_checker.class%" public="false" />
 
-        <service id="security.encoder.sha1" class="%security.encoder.digest.class%" public="false">
-            <argument>sha1</argument>
-        </service>
-
-        <service id="security.encoder.md5" class="%security.encoder.digest.class%" public="false">
-            <argument>md5</argument>
+        <service id="security.encoder_factory.generic" class="%security.encoder_factory.generic.class%" public="false">
+            <argument type="collection"></argument>
         </service>
-
-        <service id="security.encoder.plain" class="%security.encoder.plain.class%" public="false" />
+        
+        <service id="security.encoder_factory" alias="security.encoder_factory.generic" public="false"></service>
 
         <service id="security.logout.handler.session" class="%security.logout.handler.session.class%" public="false"></service>
 

+ 6 - 10
src/Symfony/Component/Security/Authentication/Provider/DaoAuthenticationProvider.php

@@ -2,11 +2,10 @@
 
 namespace Symfony\Component\Security\Authentication\Provider;
 
+use Symfony\Component\Security\Encoder\EncoderFactoryInterface;
 use Symfony\Component\Security\User\UserProviderInterface;
 use Symfony\Component\Security\User\AccountCheckerInterface;
 use Symfony\Component\Security\User\AccountInterface;
-use Symfony\Component\Security\Encoder\PasswordEncoderInterface;
-use Symfony\Component\Security\Encoder\PlaintextPasswordEncoder;
 use Symfony\Component\Security\Exception\UsernameNotFoundException;
 use Symfony\Component\Security\Exception\AuthenticationServiceException;
 use Symfony\Component\Security\Exception\BadCredentialsException;
@@ -29,7 +28,7 @@ use Symfony\Component\Security\Authentication\Token\UsernamePasswordToken;
  */
 class DaoAuthenticationProvider extends UserAuthenticationProvider
 {
-    protected $passwordEncoder;
+    protected $encoderFactory;
     protected $userProvider;
 
     /**
@@ -37,16 +36,13 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
      *
      * @param UserProviderInterface    $userProvider    A UserProviderInterface instance
      * @param AccountCheckerInterface  $accountChecker  An AccountCheckerInterface instance
-     * @param PasswordEncoderInterface $passwordEncoder A PasswordEncoderInterface instance
+     * @param EncoderFactoryInterface  $encoderFactory  A EncoderFactoryInterface instance
      */
-    public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker, PasswordEncoderInterface $passwordEncoder = null, $hideUserNotFoundExceptions = true)
+    public function __construct(UserProviderInterface $userProvider, AccountCheckerInterface $accountChecker, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true)
     {
         parent::__construct($accountChecker, $hideUserNotFoundExceptions);
 
-        if (null === $passwordEncoder) {
-            $passwordEncoder = new PlaintextPasswordEncoder();
-        }
-        $this->passwordEncoder = $passwordEncoder;
+        $this->encoderFactory = $encoderFactory;
         $this->userProvider = $userProvider;
     }
 
@@ -65,7 +61,7 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
                 throw new BadCredentialsException('Bad credentials');
             }
 
-            if (!$this->passwordEncoder->isPasswordValid($account->getPassword(), $presentedPassword, $account->getSalt())) {
+            if (!$this->encoderFactory->getEncoder($account)->isPasswordValid($account->getPassword(), $presentedPassword, $account->getSalt())) {
                 throw new BadCredentialsException('Bad credentials');
             }
         }

+ 77 - 0
src/Symfony/Component/Security/Encoder/EncoderFactory.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace Symfony\Component\Security\Encoder;
+
+use Symfony\Component\Security\User\AccountInterface;
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * A generic encoder factory implementation
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class EncoderFactory implements EncoderFactoryInterface
+{
+    protected $encoders;
+    protected $encoderMap;
+
+    public function __construct(array $encoderMap)
+    {
+        $this->encoders = array();
+        $this->encoderMap = $encoderMap;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getEncoder(AccountInterface $account)
+    {
+        foreach ($this->encoders as $class => $encoder) {
+            if ($account instanceof $class) {
+                return $encoder;
+            }
+        }
+
+        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;
+    }
+
+    /**
+     * Creates the actual encoder instance
+     *
+     * @param AccountInterface $account
+     * @return PasswordEncoderInterface
+     */
+    protected function createEncoder($account)
+    {
+        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];
+            }
+        }
+
+        throw new \InvalidArgumentException(sprintf('No encoder has been configured for account "%s".', get_class($account)));
+    }
+}

+ 30 - 0
src/Symfony/Component/Security/Encoder/EncoderFactoryInterface.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Symfony\Component\Security\Encoder;
+
+use Symfony\Component\Security\User\AccountInterface;
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * EncoderFactoryInterface to support different encoders for different accounts.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+interface EncoderFactoryInterface
+{
+    /**
+     * Returns the password encoder to use for the given account
+     *
+     * @param AccountInterface $account
+     * @return PasswordEncoderInterface never null
+     */
+    function getEncoder(AccountInterface $account);
+}

+ 20 - 5
tests/Symfony/Tests/Component/Security/Authentication/Provider/DaoAuthenticationProviderTest.php

@@ -10,6 +10,10 @@
 
 namespace Symfony\Tests\Component\Security\Authentication\Provider;
 
+use Symfony\Component\Security\Encoder\EncoderFactory;
+
+use Symfony\Component\Security\Encoder\PlaintextPasswordEncoder;
+
 use Symfony\Component\Security\Authentication\Provider\DaoAuthenticationProvider;
 
 class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
@@ -37,7 +41,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
                      ->will($this->throwException($this->getMock('Symfony\Component\Security\Exception\UsernameNotFoundException', null, array(), '', false)))
         ;
 
-        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'));
+        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
         $method = new \ReflectionMethod($provider, 'retrieveUser');
         $method->setAccessible(true);
 
@@ -55,7 +59,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
                      ->will($this->throwException($this->getMock('RuntimeException', null, array(), '', false)))
         ;
 
-        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'));
+        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
         $method = new \ReflectionMethod($provider, 'retrieveUser');
         $method->setAccessible(true);
 
@@ -76,7 +80,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
               ->will($this->returnValue($user))
         ;
 
-        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'));
+        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
         $reflection = new \ReflectionMethod($provider, 'retrieveUser');
         $reflection->setAccessible(true);
         $result = $reflection->invoke($provider, null, $token);
@@ -94,7 +98,7 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
                      ->will($this->returnValue($user))
         ;
 
-        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'));
+        $provider = new DaoAuthenticationProvider($userProvider, $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface'), $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface'));
         $method = new \ReflectionMethod($provider, 'retrieveUser');
         $method->setAccessible(true);
 
@@ -236,6 +240,17 @@ class DaoAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
             $userChecker = $this->getMock('Symfony\Component\Security\User\AccountCheckerInterface');
         }
 
-        return new DaoAuthenticationProvider($userProvider, $userChecker, $passwordEncoder);
+        if (null === $passwordEncoder) {
+            $passwordEncoder = new PlaintextPasswordEncoder();
+        }
+
+        $encoderFactory = $this->getMock('Symfony\Component\Security\Encoder\EncoderFactoryInterface');
+        $encoderFactory
+            ->expects($this->any())
+            ->method('getEncoder')
+            ->will($this->returnValue($passwordEncoder))
+        ;
+
+        return new DaoAuthenticationProvider($userProvider, $userChecker, $encoderFactory);
     }
 }

+ 33 - 0
tests/Symfony/Tests/Component/Security/Encoder/EncoderFactoryTest.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Symfony\Tests\Component\Security\Encoder;
+
+use Symfony\Component\Security\Encoder\MessageDigestPasswordEncoder;
+use Symfony\Component\Security\Encoder\EncoderFactory;
+
+class EncoderFactoryTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetEncoderWithMessageDigestEncoder()
+    {
+        $factory = new EncoderFactory(array('Symfony\Component\Security\User\AccountInterface' => array(
+            'class' => 'Symfony\Component\Security\Encoder\MessageDigestPasswordEncoder',
+            'arguments' => array('sha512', true, 5),
+        )));
+
+        $encoder = $factory->getEncoder($this->getMock('Symfony\Component\Security\User\AccountInterface'));
+        $expectedEncoder = new MessageDigestPasswordEncoder('sha512', true, 5);
+
+        $this->assertEquals($expectedEncoder->encodePassword('foo', 'moo'), $encoder->encodePassword('foo', 'moo'));
+    }
+
+    public function testGetEncoderWithService()
+    {
+        $factory = new EncoderFactory(array());
+        $factory->addEncoder('Symfony\Component\Security\User\AccountInterface', new MessageDigestPasswordEncoder('sha1'));
+
+        $encoder = $factory->getEncoder($this->getMock('Symfony\Component\Security\User\AccountInterface'));
+        $expectedEncoder = new MessageDigestPasswordEncoder('sha1');
+
+        $this->assertEquals($expectedEncoder->encodePassword('foo', ''), $encoder->encodePassword('foo', ''));
+    }
+}