diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php index cd4158dfa5d8c..8bd389dc95b4c 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php @@ -27,7 +27,7 @@ class FormLoginLdapFactory extends FormLoginFactory protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId) { $provider = 'security.authentication.provider.ldap_bind.'.$id; - $container + $definition = $container ->setDefinition($provider, new ChildDefinition('security.authentication.provider.ldap_bind')) ->replaceArgument(0, new Reference($userProviderId)) ->replaceArgument(1, new Reference('security.user_checker.'.$id)) @@ -36,6 +36,10 @@ protected function createAuthProvider(ContainerBuilder $container, $id, $config, ->replaceArgument(4, $config['dn_string']) ; + if (!empty($config['query_string'])) { + $definition->addMethodCall('setQueryString', array($config['query_string'])); + } + return $provider; } @@ -47,6 +51,7 @@ public function addConfiguration(NodeDefinition $node) ->children() ->scalarNode('service')->defaultValue('ldap')->end() ->scalarNode('dn_string')->defaultValue('{username}')->end() + ->scalarNode('query_string')->end() ->end() ; } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php index 961af71461a59..ce3cdda96eafd 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php @@ -28,7 +28,7 @@ class HttpBasicLdapFactory extends HttpBasicFactory public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) { $provider = 'security.authentication.provider.ldap_bind.'.$id; - $container + $definition = $container ->setDefinition($provider, new ChildDefinition('security.authentication.provider.ldap_bind')) ->replaceArgument(0, new Reference($userProvider)) ->replaceArgument(1, new Reference('security.user_checker.'.$id)) @@ -40,6 +40,10 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, // entry point $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); + if (!empty($config['query_string'])) { + $definition->addMethodCall('setQueryString', array($config['query_string'])); + } + // listener $listenerId = 'security.authentication.listener.basic.'.$id; $listener = $container->setDefinition($listenerId, new ChildDefinition('security.authentication.listener.basic')); @@ -57,6 +61,7 @@ public function addConfiguration(NodeDefinition $node) ->children() ->scalarNode('service')->defaultValue('ldap')->end() ->scalarNode('dn_string')->defaultValue('{username}')->end() + ->scalarNode('query_string')->end() ->end() ; } diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php index 5ebb09ab3dad4..9c07fd5a35697 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -33,6 +33,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider private $userProvider; private $ldap; private $dnString; + private $queryString; /** * Constructor. @@ -53,6 +54,16 @@ public function __construct(UserProviderInterface $userProvider, UserCheckerInte $this->dnString = $dnString; } + /** + * Set a query string to use in order to find a DN for the username. + * + * @param string $queryString + */ + public function setQueryString($queryString) + { + $this->queryString = $queryString; + } + /** * {@inheritdoc} */ @@ -79,7 +90,18 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke try { $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN); - $dn = str_replace('{username}', $username, $this->dnString); + + if ($this->queryString) { + $query = str_replace('{username}', $username, $this->queryString); + $result = $this->ldap->query($this->dnString, $query)->execute(); + if (1 !== $result->count()) { + throw new BadCredentialsException('The presented username is invalid.'); + } + + $dn = $result[0]->getDn(); + } else { + $dn = str_replace('{username}', $username, $this->dnString); + } $this->ldap->bind($dn, $password); } catch (ConnectionException $e) { diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php index 9359f869f02f3..51850c463270c 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php @@ -12,6 +12,9 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Provider; use Symfony\Component\Ldap\LdapInterface; +use Symfony\Component\Ldap\Entry; +use Symfony\Component\Ldap\Adapter\QueryInterface; +use Symfony\Component\Ldap\Adapter\CollectionInterface; use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\User\User; @@ -81,4 +84,73 @@ public function testRetrieveUser() $reflection->invoke($provider, 'foo', new UsernamePasswordToken('foo', 'bar', 'key')); } + + public function testQueryForDn() + { + $userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMock(); + + $collection = new \ArrayIterator(array(new Entry(''))); + + $query = $this->getMockBuilder(QueryInterface::class)->getMock(); + $query + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue($collection)) + ; + + $ldap = $this->getMockBuilder(LdapInterface::class)->getMock(); + $ldap + ->expects($this->once()) + ->method('escape') + ->with('foo', '') + ->will($this->returnValue('foo')) + ; + $ldap + ->expects($this->once()) + ->method('query') + ->with('{username}', 'foobar') + ->will($this->returnValue($query)) + ; + $userChecker = $this->getMockBuilder(UserCheckerInterface::class)->getMock(); + + $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); + $provider->setQueryString('{username}bar'); + $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); + $reflection->setAccessible(true); + + $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException + * @expectedExceptionMessage The presented username is invalid. + */ + public function testEmptyQueryResultShouldThrowAnException() + { + $userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMock(); + + $collection = $this->getMockBuilder(CollectionInterface::class)->getMock(); + + $query = $this->getMockBuilder(QueryInterface::class)->getMock(); + $query + ->expects($this->once()) + ->method('execute') + ->will($this->returnValue($collection)) + ; + + $ldap = $this->getMockBuilder(LdapInterface::class)->getMock(); + $ldap + ->expects($this->once()) + ->method('query') + ->will($this->returnValue($query)) + ; + $userChecker = $this->getMockBuilder(UserCheckerInterface::class)->getMock(); + + $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); + $provider->setQueryString('{username}bar'); + $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); + $reflection->setAccessible(true); + + $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); + } }