Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bcf8b68

Browse files
committed
feature #21402 [Security] make LdapBindAuthenticationProvider capable of searching for the DN (lsmith77, nietonfir)
This PR was merged into the 3.3-dev branch. Discussion ---------- [Security] make LdapBindAuthenticationProvider capable of searching for the DN | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #16823, #20905 | License | MIT | Doc PR | symfony/symfony-docs#7420 I guess due to the separation between the user and auth provider something like the following isn't ok (note: the following works just fine for me but if course in the end the username is the DN and not the user provided shorter username): ```diff diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php index 5ebb09a..18d7825 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -70,7 +70,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider */ protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) { - $username = $token->getUsername(); + $username = $user->getUsername(); $password = $token->getCredentials(); if ('' === $password) { @@ -78,10 +78,7 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider } try { - $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN); - $dn = str_replace('{username}', $username, $this->dnString); - - $this->ldap->bind($dn, $password); + $this->ldap->bind($username, $password); } catch (ConnectionException $e) { throw new BadCredentialsException('The presented password is invalid.'); } diff --git a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php index fc42419..8194c4c 100644 --- a/src/Symfony/Component/Security/Core/User/LdapUserProvider.php +++ b/src/Symfony/Component/Security/Core/User/LdapUserProvider.php @@ -115,7 +115,7 @@ class LdapUserProvider implements UserProviderInterface { $password = $this->getPassword($entry); - return new User($username, $password, $this->defaultRoles); + return new User($entry->getDn(), $password, $this->defaultRoles); } /** ``` Therefore I created an entire new auth provider. Commits ------- 8ddd533 Merge pull request #1 from nietonfir/http_basic_ldap a783e5c Update HttpBasicLdapFactory a30191f make LdapBindAuthenticationProvider capable of searching for the DN
2 parents f025392 + 8ddd533 commit bcf8b68

File tree

4 files changed

+107
-3
lines changed

4 files changed

+107
-3
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/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class HttpBasicLdapFactory extends HttpBasicFactory
2828
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
2929
{
3030
$provider = 'security.authentication.provider.ldap_bind.'.$id;
31-
$container
31+
$definition = $container
3232
->setDefinition($provider, new ChildDefinition('security.authentication.provider.ldap_bind'))
3333
->replaceArgument(0, new Reference($userProvider))
3434
->replaceArgument(1, new Reference('security.user_checker.'.$id))
@@ -40,6 +40,10 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider,
4040
// entry point
4141
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint);
4242

43+
if (!empty($config['query_string'])) {
44+
$definition->addMethodCall('setQueryString', array($config['query_string']));
45+
}
46+
4347
// listener
4448
$listenerId = 'security.authentication.listener.basic.'.$id;
4549
$listener = $container->setDefinition($listenerId, new ChildDefinition('security.authentication.listener.basic'));
@@ -57,6 +61,7 @@ public function addConfiguration(NodeDefinition $node)
5761
->children()
5862
->scalarNode('service')->defaultValue('ldap')->end()
5963
->scalarNode('dn_string')->defaultValue('{username}')->end()
64+
->scalarNode('query_string')->end()
6065
->end()
6166
;
6267
}

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

Lines changed: 23 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,18 @@ 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+
$result = $this->ldap->query($this->dnString, $query)->execute();
97+
if (1 !== $result->count()) {
98+
throw new BadCredentialsException('The presented username is invalid.');
99+
}
100+
101+
$dn = $result[0]->getDn();
102+
} else {
103+
$dn = str_replace('{username}', $username, $this->dnString);
104+
}
83105

84106
$this->ldap->bind($dn, $password);
85107
} 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)