Skip to content

Commit 7d7e07f

Browse files
committed
feature #14602 [2.8] [Ldap] Added support for LDAP (New Component + integration in the Security Component). (csarrazi, lyrixx)
This PR was merged into the 2.8 branch. Discussion ---------- [2.8] [Ldap] Added support for LDAP (New Component + integration in the Security Component). | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | not yet | Fixed tickets | - | License | MIT | Doc PR | not yet Current state: - [x] Implement logic - [x] Post-review tuning and stabilization - [x] Fix tests This PR is a follow-up to #5189, which was in a stand-still for a few years now. It tries to fix the remaining issues which were mentioned in the discussion. There are still a few issues with the PR, as it is. For example, it introduces two new firewall factories, whereas the base factories (`form_login` and `http_basic`) could simply introduce new configuration options. Also, for a user to use an LDAP server as an authentication provider, he first needs to define a service which should be an instance of `Symfony\Component\Security\Ldap\Ldap`. For example: ```yml services: my_ldap: class: Symfony\Component\Security\Ldap\Ldap arguments: [ "ldap.mydomain.tld" ] ``` Then, in `security.yml`, this service can be used in both the user provider and the firewalls: ```yml security: encoders: Symfony\Component\Security\Core\User\User: plaintext role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] providers: ldap_users: ldap: service: my_ldap base_dn: dc=MyDomain,dc=tld search_dn: CN=My User,OU=Users,DC=MyDomain,DC=tld search_password: p455w0rd filter: (sAMAccountName={username}) default_roles: ROLE_USER firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false demo_login: pattern: ^/login$ security: false api: provider: ldap_users stateless: true pattern: ^/api http_basic_ldap: service: my_ldap dn_string: "{username}@mydomain" demo_secured_area: provider: ldap_users pattern: ^/ logout: path: logout target: login form_login_ldap: service: my_ldap dn_string: CN={username},OU=Users,DC=MyDomain,DC=tld check_path: login_check login_path: login ``` Commits ------- 60b9f2e Implemented LDAP authentication and LDAP user provider 1c964b9 Introducing the LDAP component
2 parents 32002d7 + 60b9f2e commit 7d7e07f

24 files changed

+1037
-3
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ before_install:
3535
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi;
3636
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]] && [ "$deps" = "no" ]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo "extension = $(pwd)/modules/symfony_debug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini); fi;
3737
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then php -i; fi;
38+
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then echo "extension = ldap.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
3839
- ./phpunit install
3940
- export PHPUNIT="$(readlink -f ./phpunit)"
4041

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ install:
4545
- IF %PHP_EXT%==1 echo extension=php_mbstring.dll >> php.ini
4646
- IF %PHP_EXT%==1 echo extension=php_fileinfo.dll >> php.ini
4747
- IF %PHP_EXT%==1 echo extension=php_pdo_sqlite.dll >> php.ini
48+
- IF %PHP_EXT%==1 echo extension=php_ldap.dll >> php.ini
4849
- cd c:\projects\symfony
4950
- php phpunit install
5051
- IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev)
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
}

src/Symfony/Component/Ldap/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Component\Ldap\Exception;
13+
14+
/**
15+
* ConnectionException is throw if binding to ldap can not be established.
16+
*
17+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
18+
*/
19+
class ConnectionException extends \RuntimeException
20+
{
21+
}

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