Skip to content

[WIP][Security] Added Ldap provider #5189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;

use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* FormLoginLdapFactory creates services for form login ldap authentication.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class FormLoginLdapFactory extends FormLoginFactory
{
protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId)
{
$provider = 'security.authentication.provider.ldap.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap'))
->replaceArgument(0, new Reference($userProviderId))
->replaceArgument(2, $id)
;

return $provider;
}

public function getKey()
{
return 'form-login-ldap';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;

use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* HttpBasicFactory creates services for HTTP basic authentication.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class HttpBasicLdapFactory extends HttpBasicFactory
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$provider = 'security.authentication.provider.ldap.'.$id;
$container
->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap'))
->replaceArgument(0, new Reference($userProvider))
->replaceArgument(2, $id)
;

// entry point
$entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint);

// listener
$listenerId = 'security.authentication.listener.basic.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic'));
$listener->replaceArgument(2, $id);
$listener->replaceArgument(3, new Reference($entryPointId));

return array($provider, $listenerId, $entryPointId);
}

public function getKey()
{
return 'http-basic-ldap';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider;

use Symfony\Component\Config\Definition\Builder\NodeDefinition;

use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* LdapFactory creates services for Ldap user provider.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class LdapFactory implements UserProviderFactoryInterface
{
private $key;
private $providerId;

public function create(ContainerBuilder $container, $id, $config)
{
$container
->setDefinition('security.ldap.ldap.'.$id , new DefinitionDecorator('security.ldap.ldap'))
->addArgument($config['host'])
->addArgument($config['port'])
->addArgument($config['dn'])
->addArgument($config['username_suffix'])
->addArgument($config['version'])
->addArgument($config['use_ssl'])
->addArgument($config['use_start_tls'])
->addArgument($config['opt_referrals'])
;

$container
->setDefinition($id, new DefinitionDecorator('security.user.provider.ldap'))
->replaceArgument(0, new Reference('security.ldap.ldap.'.$id))
->replaceArgument(1, $config['default_roles'])
;
}

public function getKey()
{
return 'ldap';
}

public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('host')->isRequired()->cannotBeEmpty()->end()
->scalarNode('port')->cannotBeEmpty()->defaultValue(389)->end()
->scalarNode('dn')->isRequired()->cannotBeEmpty()->end()
->scalarNode('username_suffix')->defaultValue('')->end()
->scalarNode('version')->defaultValue(3)->end()
->scalarNode('use_ssl')->defaultFalse()->end()
->scalarNode('use_start_tls')->defaultFalse()->end()
->scalarNode('opt_referrals')->defaultFalse()->end()
->arrayNode('default_roles')
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()
->end()
->end()
;
}
}
11 changes: 11 additions & 0 deletions src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

<parameter key="security.user.provider.in_memory.class">Symfony\Component\Security\Core\User\InMemoryUserProvider</parameter>
<parameter key="security.user.provider.in_memory.user.class">Symfony\Component\Security\Core\User\User</parameter>
<parameter key="security.user.provider.ldap.class">Symfony\Component\Security\Core\User\LdapUserProvider</parameter>
<parameter key="security.user.provider.chain.class">Symfony\Component\Security\Core\User\ChainUserProvider</parameter>

<parameter key="security.authentication.trust_resolver.class">Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver</parameter>
Expand All @@ -41,6 +42,8 @@
<parameter key="security.http_utils.class">Symfony\Component\Security\Http\HttpUtils</parameter>

<parameter key="security.validator.user_password.class">Symfony\Component\Security\Core\Validator\Constraint\UserPasswordValidator</parameter>

<parameter key="security.ldap.ldap.class">Symfony\Component\Security\Ldap\Ldap</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -124,6 +127,11 @@
<service id="security.user.provider.in_memory" class="%security.user.provider.in_memory.class%" abstract="true" public="false" />
<service id="security.user.provider.in_memory.user" class="%security.user.provider.in_memory.user.class%" abstract="true" public="false" />

<service id="security.user.provider.ldap" class="%security.user.provider.ldap.class%" abstract="true" public="false">
<argument /> <!-- security.ldap.ldap -->
<argument /> <!-- default_roles -->
</service>

<service id="security.user.provider.chain" class="%security.user.provider.chain.class%" abstract="true" public="false" />

<service id="security.http_utils" class="%security.http_utils.class%" public="false">
Expand All @@ -137,5 +145,8 @@
<argument type="service" id="security.context" />
<argument type="service" id="security.encoder_factory" />
</service>

<!-- Other -->
<service id="security.ldap.ldap" class="%security.ldap.ldap.class%" abstract="true" public="false"/>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<parameter key="security.context_listener.class">Symfony\Component\Security\Http\Firewall\ContextListener</parameter>

<parameter key="security.authentication.provider.dao.class">Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider</parameter>
<parameter key="security.authentication.provider.ldap.class">Symfony\Component\Security\Core\Authentication\Provider\LdapAuthenticationProvider</parameter>
<parameter key="security.authentication.provider.pre_authenticated.class">Symfony\Component\Security\Core\Authentication\Provider\PreAuthenticatedAuthenticationProvider</parameter>

<parameter key="security.authentication.provider.anonymous.class">Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider</parameter>
Expand Down Expand Up @@ -170,6 +171,13 @@
<argument>%security.authentication.hide_user_not_found%</argument>
</service>

<service id="security.authentication.provider.ldap" class="%security.authentication.provider.ldap.class%" abstract="true" public="false">
<argument /> <!-- User Provider -->
<argument type="service" id="security.user_checker" />
<argument /> <!-- Provider-shared Key -->
<argument>%security.authentication.hide_user_not_found%</argument>
</service>

<service id="security.authentication.provider.pre_authenticated" class="%security.authentication.provider.pre_authenticated.class%" abstract="true" public="false">
<argument /> <!-- User Provider -->
<argument type="service" id="security.user_checker" />
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Bundle/SecurityBundle/SecurityBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicLdapFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\LdapFactory;

/**
* Bundle.
Expand All @@ -34,12 +37,15 @@ public function build(ContainerBuilder $container)

$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new FormLoginFactory());
$extension->addSecurityListenerFactory(new FormLoginLdapFactory());
$extension->addSecurityListenerFactory(new HttpBasicFactory());
$extension->addSecurityListenerFactory(new HttpBasicLdapFactory());
$extension->addSecurityListenerFactory(new HttpDigestFactory());
$extension->addSecurityListenerFactory(new RememberMeFactory());
$extension->addSecurityListenerFactory(new X509Factory());

$extension->addUserProviderFactory(new InMemoryFactory());
$extension->addUserProviderFactory(new LdapFactory());
$container->addCompilerPass(new AddSecurityVotersPass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Symfony\Component\Security\Core\Authentication\Provider;

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\LdapUserProviderInterface;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
* LdapAuthenticationProvider uses a LdapUserProviderInterface to retrieve the user
* for a UsernamePasswordToken.
*
* The only way to check user credentials is to try to connect the user with its
* credentials to the ldap.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class LdapAuthenticationProvider extends UserAuthenticationProvider
{
private $userProvider;
private $ldap;

/**
* Constructor.
*
* @param LdapUserProviderInterface $userProvider An LdapUserProvider instance
* @param UserCheckerInterface $userChecker An UserCheckerInterface instance
* @param string $providerKey The provider key
* @param Boolean $hideUserNotFoundExceptions Whether to hide user not found exception or not
*/
public function __construct(LdapUserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, $hideUserNotFoundExceptions = true)
{
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);

$this->userProvider = $userProvider;
}

/**
* {@inheritdoc}
*/
protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
{
if ("" === ($presentedPassword = $token->getCredentials())) {
throw new BadCredentialsException('The presented password cannot be empty.');
}

// At this point, the $user is already authenticated
}

/**
* {@inheritdoc}
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
$user = $token->getUser();
if ($user instanceof UserInterface) {
return $user;
}

try {
$user = $this->userProvider->loadUserByUsernameAndPassword($username, $token->getCredentials());

if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
}

return $user;
} catch (UsernameNotFoundException $notFound) {
throw $notFound;
}
}
}
Loading