8000 feature #14602 [2.8] [Ldap] Added support for LDAP (New Component + i… · symfony/symfony@7d7e07f · GitHub
[go: up one dir, main page]

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+
}
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+
* LdapException is throw if php ldap module is not loaded.
16+
*
17+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
18+
*/
19+
class LdapException extends \RuntimeException
20+
{
21+
}

src/Symfony/Component/Ldap/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2004-2015 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

0 commit comments

Comments
 (0)
0