Skip to content

Commit 60b9f2e

Browse files
lyrixxcsarrazi
authored andcommitted
Implemented LDAP authentication and LDAP user provider
1 parent 1c964b9 commit 60b9f2e

File tree

12 files changed

+561
-3
lines changed

12 files changed

+561
-3
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
13+
14+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
15+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
/**
20+
* FormLoginLdapFactory creates services for form login ldap authentication.
21+
*
22+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
23+
* @author Charles Sarrazin <charles@sarraz.in>
24+
*/
25+
class FormLoginLdapFactory extends FormLoginFactory
26+
{
27+
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
28+
{
29+
$provider = 'security.authentication.provider.ldap_bind.'.$id;
30+
$container
31+
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap_bind'))
32+
->replaceArgument(0, new Reference($userProviderId))
33+
->replaceArgument(2, $id)
34+
->replaceArgument(3, new Reference($config['service']))
35+
->replaceArgument(4, $config['dn_string'])
36+
;
37+
38+
return $provider;
39+
}
40+
41+
public function addConfiguration(NodeDefinition $node)
42+
{
43+
parent::addConfiguration($node);
44+
45+
$node
46+
->children()
47+
->scalarNode('service')->end()
48+
->scalarNode('dn_string')->defaultValue('{username}')->end()
49+
->end()
50+
;
51+
}
52+
53+
public function getKey()
54+
{
55+
return 'form-login-ldap';
56+
}
57+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
13+
14+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
15+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
/**
20+
* HttpBasicFactory creates services for HTTP basic authentication.
21+
*
22+
* @author Fabien Potencier <fabien@symfony.com>
23+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
24+
* @author Charles Sarrazin <charles@sarraz.in>
25+
*/
26+
class HttpBasicLdapFactory extends HttpBasicFactory
27+
{
28+
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
29+
{
30+
$provider = 'security.authentication.provider.ldap_bind.'.$id;
31+
$container
32+
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap_bind'))
33+
->replaceArgument(0, new Reference($userProvider))
34+
->replaceArgument(2, $id)
35+
->replaceArgument(3, new Reference($config['service']))
36+
->replaceArgument(4, $config['dn_string'])
37+
;
38+
39+
// entry point
40+
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint);
41+
42+
// listener
43+
$listenerId = 'security.authentication.listener.basic.'.$id;
44+
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic'));
45+
$listener->replaceArgument(2, $id);
46+
$listener->replaceArgument(3, new Reference($entryPointId));
47+
48+
return array($provider, $listenerId, $entryPointId);
49+
}
50+
51+
public function addConfiguration(NodeDefinition $node)
52+
{
53+
parent::addConfiguration($node);
54+
55+
$node
56+
->children()
57+
->scalarNode('service')->end()
58+
->scalarNode('dn_string')->defaultValue('{username}')->end()
59+
->end()
60+
;
61+
}
62+
63+
public function getKey()
64+
{
65+
return 'http-basic-ldap';
66+
}
67+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider;
13+
14+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
15+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
/**
20+
* LdapFactory creates services for Ldap user provider.
21+
*
22+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
23+
* @author Charles Sarrazin <charles@sarraz.in>
24+
*/
25+
class LdapFactory implements UserProviderFactoryInterface
26+
{
27+
public function create(ContainerBuilder $container, $id, $config)
28+
{
29+
$container
30+
->setDefinition($id, new DefinitionDecorator('security.user.provider.ldap'))
31+
->replaceArgument(0, new Reference($config['service']))
32+
->replaceArgument(1, $config['base_dn'])
33+
->replaceArgument(2, $config['search_dn'])
34+
->replaceArgument(3, $config['search_password'])
35+
->replaceArgument(4, $config['default_roles'])
36+
->replaceArgument(5, $config['uid_key'])
37+
->replaceArgument(6, $config['filter'])
38+
;
39+
}
40+
41+
public function getKey()
42+
{
43+
return 'ldap';
44+
}
45+
46+
public function addConfiguration(NodeDefinition $node)
47+
{
48+
$node
49+
->children()
50+
->scalarNode('service')->isRequired()->cannotBeEmpty()->end()
51+
->scalarNode('base_dn')->isRequired()->cannotBeEmpty()->end()
52+
->scalarNode('search_dn')->end()
53+
->scalarNode('search_password')->end()
54+
->arrayNode('default_roles')
55+
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
56+
->requiresAtLeastOneElement()
57+
->prototype('scalar')->end()
58+
->end()
59+
->scalarNode('uid_key')->defaultValue('sAMAccountName')->end()
60+
->scalarNode('filter')->defaultValue('({uid_key}={username})')->end()
61+
->end()
62+
;
63+
}
64+
}

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,29 @@
158158
<argument type="service" id="security.token_storage" on-invalid="null" />
159159
</service>
160160

161+
161162
<!-- Provisioning -->
162163
<service id="security.user.provider.in_memory" class="%security.user.provider.in_memory.class%" abstract="true" public="false" />
163164
<service id="security.user.provider.in_memory.user" class="%security.user.provider.in_memory.user.class%" abstract="true" public="false" />
164165

166+
<service id="security.user.provider.ldap" class="Symfony\Component\Security\Core\User\LdapUserProvider" abstract="true" public="false">
167+
<argument /> <!-- security.ldap.ldap -->
168+
<argument /> <!-- base dn -->
169+
<argument /> <!-- search dn -->
170+
<argument /> <!-- search password -->
171+
<argument /> <!-- default_roles -->
172+
<argument /> <!-- uid key -->
173+
<argument /> <!-- filter -->
174+
</service>
175+
165176
<service id="security.user.provider.chain" class="%security.user.provider.chain.class%" abstract="true" public="false" />
166177

167178
<service id="security.http_utils" class="%security.http_utils.class%" public="false">
168179
<argument type="service" id="router" on-invalid="null" />
169180
<argument type="service" id="router" on-invalid="null" />
170181
</service>
171182

183+
172184
<!-- Validator -->
173185
<service id="security.validator.user_password" class="%security.validator.user_password.class%">
174186
<tag name="validator.constraint_validator" alias="security.validator.user_password" />

src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@
223223
<argument>%security.authentication.hide_user_not_found%</argument>
224224
</service>
225225

226+
<service id="security.authentication.provider.ldap_bind" class="Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider" public="false" abstract="true">
227+
<argument /> <!-- User Provider -->
228+
<argument type="service" id="security.user_checker" />
229+
<argument /> <!-- Provider-shared Key -->
230+
<argument /> <!-- LDAP -->
231+
<argument /> <!-- Base DN -->
232+
<argument>%security.authentication.hide_user_not_found%</argument>
233+
</service>
234+
226235
<service id="security.authentication.provider.simple" class="%security.authentication.provider.simple.class%" abstract="true" public="false">
227236
<argument /> <!-- Simple Authenticator -->
228237
<argument /> <!-- User Provider -->

src/Symfony/Bundle/SecurityBundle/SecurityBundle.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
1717
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
18+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
1819
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory;
20+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicLdapFactory;
1921
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory;
2022
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
2123
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory;
@@ -24,6 +26,7 @@
2426
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory;
2527
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;
2628
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
29+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\LdapFactory;
2730

2831
/**
2932
* Bundle.
@@ -38,7 +41,9 @@ public function build(ContainerBuilder $container)
3841

3942
$extension = $container->getExtension('security');
4043
$extension->addSecurityListenerFactory(new FormLoginFactory());
44+
$extension->addSecurityListenerFactory(new FormLoginLdapFactory());
4145
$extension->addSecurityListenerFactory(new HttpBasicFactory());
46+
$extension->addSecurityListenerFactory(new HttpBasicLdapFactory());
4247
$extension->addSecurityListenerFactory(new HttpDigestFactory());
4348
$extension->addSecurityListenerFactory(new RememberMeFactory());
4449
$extension->addSecurityListenerFactory(new X509Factory());
@@ -48,6 +53,7 @@ public function build(ContainerBuilder $container)
4853
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
4954

5055
$extension->addUserProviderFactory(new InMemoryFactory());
56+
$extension->addUserProviderFactory(new LdapFactory());
5157
$container->addCompilerPass(new AddSecurityVotersPass());
5258
}
5359
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
namespace Symfony\Component\Security\Core\Authentication\Provider;
4+
5+
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
6+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
7+
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
8+
use Symfony\Component\Security\Core\User\UserCheckerInterface;
9+
use Symfony\Component\Security\Core\User\UserInterface;
10+
use Symfony\Component\Security\Core\User\UserProviderInterface;
11+
use Symfony\Component\Ldap\LdapClientInterface;
12+
use Symfony\Component\Ldap\Exception\ConnectionException;
13+
14+
/**
15+
* LdapBindAuthenticationProvider authenticates a user against an LDAP server.
16+
*
17+
* The only way to check user credentials is to try to connect the user with its
18+
* credentials to the ldap.
19+
*
20+
* @author Charles Sarrazin <charles@sarraz.in>
21+
*/
22+
class LdapBindAuthenticationProvider extends UserAuthenticationProvider
23+
{
24+
private $userProvider;
25+
private $ldap;
26+
private $dnString;
27+
28+
/**
29+
* Constructor.
30+
*
31+
* @param UserProviderInterface $userProvider A UserProvider
32+
* @param UserCheckerInterface $userChecker A UserChecker
33+
* @param string $providerKey The provider key
34+
* @param LdapClientInterface $ldap An Ldap client
35+
* @param string $dnString A string used to create the bind DN
36+
* @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not
37+
*/
38+
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, LdapClientInterface $ldap, $dnString = '{username}', $hideUserNotFoundExceptions = true)
39+
{
40+
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
41+
42+
$this->userProvider = $userProvider;
43+
$this->ldap = $ldap;
44+
$this->dnString = $dnString;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
protected function retrieveUser($username, UsernamePasswordToken $token)
51+
{
52+
if ('NONE_PROVIDED' === $username) {
53+
throw new UsernameNotFoundException('Username can not be null');
54+
}
55+
56+
return $this->userProvider->loadUserByUsername($username);
57+
}
58+
59+
/**
60+
* {@inheritdoc}
61+
*/
62+
protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
63+
{
64+
$username = $token->getUsername();
65+
$password = $token->getCredentials();
66+
67+
try {
68+
$username = $this->ldap->escape($username, '', LdapClientInterface::LDAP_ESCAPE_DN);
69+
$dn = str_replace('{username}', $username, $this->dnString);
70+
71+
$this->ldap->bind($dn, $password);
72+
} catch (ConnectionException $e) {
73+
throw new BadCredentialsException('The presented password is invalid.');
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy