Skip to content

Commit a30191f

Browse files
committed
make LdapBindAuthenticationProvider capable of searching for the DN
1 parent b9b6ebd commit a30191f

File tree

3 files changed

+103
-2
lines changed

3 files changed

+103
-2
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class FormLoginLdapFactory extends FormLoginFactory
2727
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
2828
{
2929
$provider = 'security.authentication.provider.ldap_bind.'.$id;
30-
$container
30+
$definition = $container
3131
->setDefinition($provider, new ChildDefinition('security.authentication.provider.ldap_bind'))
3232
->replaceArgument(0, new Reference($userProviderId))
3333
->replaceArgument(1, new Reference('security.user_checker.'.$id))
@@ -36,6 +36,10 @@ protected function createAuthProvider(ContainerBuilder $container, $id, $config,
3636
->replaceArgument(4, $config['dn_string'])
3737
;
3838

39+
if (!empty($config['query_string'])) {
40+
$definition->addMethodCall('setQueryString', array($config['query_string']));
41+
}
42+
3943
return $provider;
4044
}
4145

@@ -47,6 +51,7 @@ public function addConfiguration(NodeDefinition $node)
4751
->children()
4852
->scalarNode('service')->defaultValue('ldap')->end()
4953
->scalarNode('dn_string')->defaultValue('{username}')->end()
54+
->scalarNode('query_string')->end()
5055
->end()
5156
;
5257
}

src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
3333
private $userProvider;
3434
private $ldap;
3535
private $dnString;
36+
private $queryString;
3637

3738
/**
3839
* Constructor.
@@ -53,6 +54,16 @@ public function __construct(UserProviderInterface $userProvider, UserCheckerInte
5354
$this->dnString = $dnString;
5455
}
5556

57+
/**
58+
* Set a query string to use in order to find a DN for the username.
59+
*
60+
* @param string $queryString
61+
*/
62+
public function setQueryString($queryString)
63+
{
64+
$this->queryString = $queryString;
65+
}
66+
5667
/**
5768
* {@inheritdoc}
5869
*/
@@ -79,7 +90,20 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
7990

8091
try {
8192
$username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN);
82-
$dn = str_replace('{username}', $username, $this->dnString);
93+
94+
if ($this->queryString) {
95+
$query = str_replace('{username}', $username, $this->queryString);
96+
97+
$query = $this->ldap->query($this->dnString, $query);
98+
$result = $query->execute();
99+
if (1 !== $result->count()) {
100+
throw new BadCredentialsException('The presented username is invalid.');
101+
}
102+
103+
$dn = $result[0]->getDn();
104+
} else {
105+
$dn = str_replace('{username}', $username, $this->dnString);
106+
}
83107

84108
$this->ldap->bind($dn, $password);
85109
} catch (ConnectionException $e) {

src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
namespace Symfony\Component\Security\Core\Tests\Authentication\Provider;
1313

1414
use Symfony\Component\Ldap\LdapInterface;
15+
use Symfony\Component\Ldap\Entry;
16+
use Symfony\Component\Ldap\Adapter\QueryInterface;
17+
use Symfony\Component\Ldap\Adapter\CollectionInterface;
1518
use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider;
1619
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
1720
use Symfony\Component\Security\Core\User\User;
@@ -81,4 +84,73 @@ public function testRetrieveUser()
8184

8285
$reflection->invoke($provider, 'foo', new UsernamePasswordToken('foo', 'bar', 'key'));
8386
}
87+
88+
public function testQueryForDn()
89+
{
90+
$userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMock();
91+
92+
$collection = new \ArrayIterator(array(new Entry('')));
93+
94+
$query = $this->getMockBuilder(QueryInterface::class)->getMock();
95+
$query
96+
->expects($this->once())
97+
->method('execute')
98+
->will($this->returnValue($collection))
99+
;
100+
101+
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
102+
$ldap
103+
->expects($this->once())
104+
->method('escape')
105+
->with('foo', '')
106+
->will($this->returnValue('foo'))
107+
;
108+
$ldap
109+
->expects($this->once())
110+
->method('query')
111+
->with('{username}', 'foobar')
112+
->will($this->returnValue($query))
113+
;
114+
$userChecker = $this->getMockBuilder(UserCheckerInterface::class)->getMock();
115+
116+
$provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap);
117+
$provider->setQueryString('{username}bar');
118+
$reflection = new \ReflectionMethod($provider, 'checkAuthentication');
119+
$reflection->setAccessible(true);
120+
121+
$reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', 'bar', 'key'));
122+
}
123+
124+
/**
125+
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
126+
* @expectedExceptionMessage The presented username is invalid.
127+
*/
128+
public function testEmptyQueryResultShouldThrowAnException()
129+
{
130+
$userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMock();
131+
132+
$collection = $this->getMockBuilder(CollectionInterface::class)->getMock();
133+
134+
$query = $this->getMockBuilder(QueryInterface::class)->getMock();
135+
$query
136+
->expects($this->once())
137+
->method('execute')
138+
->will($this->returnValue($collection))
139+
;
140+
141+
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
142+
$ldap
143+
->expects($this->once())
144+
->method('query')
145+
->will($this->returnValue($query))
146+
;
147+
$userChecker = $this->getMockBuilder(UserCheckerInterface::class)->getMock();
148+
149+
$provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap);
150+
$provider->setQueryString('{username}bar');
151+
$reflection = new \ReflectionMethod($provider, 'checkAuthentication');
152+
$reflection->setAccessible(true);
153+
154+
$reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', 'bar', 'key'));
155+
}
84156
}

0 commit comments

Comments
 (0)