summaryrefslogtreecommitdiffstats
path: root/Core
diff options
context:
space:
mode:
authorTobias Schultze <webmaster@tubo-world.de>2015-09-29 16:08:28 +0200
committerTobias Schultze <webmaster@tubo-world.de>2015-09-29 16:08:28 +0200
commit493704bf17328063a6e566d912e6e063d4c60f8b (patch)
tree16e3cfaffcb041445478540434fd4e5be0b3eccc /Core
parent16223cbf326eee2a9fff59f765c218ff028e9330 (diff)
parent889a989997c4b038fb4e354e57e35ede82370581 (diff)
downloadsymfony-security-493704bf17328063a6e566d912e6e063d4c60f8b.zip
symfony-security-493704bf17328063a6e566d912e6e063d4c60f8b.tar.gz
symfony-security-493704bf17328063a6e566d912e6e063d4c60f8b.tar.bz2
Merge branch '2.8'
Conflicts: composer.json src/Symfony/Bundle/FrameworkBundle/Command/RouterApacheDumperCommand.php src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php src/Symfony/Component/ClassLoader/DebugClassLoader.php src/Symfony/Component/ClassLoader/UniversalClassLoader.php src/Symfony/Component/Console/Input/StringInput.php src/Symfony/Component/Debug/DebugClassLoader.php src/Symfony/Component/DependencyInjection/Container.php src/Symfony/Component/DependencyInjection/ContainerBuilder.php src/Symfony/Component/DependencyInjection/ContainerInterface.php src/Symfony/Component/DependencyInjection/Definition.php src/Symfony/Component/DependencyInjection/DefinitionDecorator.php src/Symfony/Component/DependencyInjection/Scope.php src/Symfony/Component/DependencyInjection/ScopeInterface.php src/Symfony/Component/DomCrawler/composer.json src/Symfony/Component/EventDispatcher/Event.php src/Symfony/Component/HttpKernel/Kernel.php src/Symfony/Component/HttpKernel/KernelInterface.php src/Symfony/Component/HttpKernel/Log/LoggerInterface.php src/Symfony/Component/HttpKernel/Log/NullLogger.php src/Symfony/Component/Security/Core/composer.json src/Symfony/Component/Security/Resources/translations/security.tr.xlf src/Symfony/Component/Security/composer.json src/Symfony/Component/Translation/Translator.php
Diffstat (limited to 'Core')
-rw-r--r--Core/Authentication/Provider/LdapBindAuthenticationProvider.php85
-rw-r--r--Core/Resources/translations/security.tr.xlf12
-rw-r--r--Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php61
-rw-r--r--Core/Tests/Authorization/Voter/AbstractVoterTest.php4
-rw-r--r--Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php42
-rw-r--r--Core/Tests/User/LdapUserProviderTest.php102
-rw-r--r--Core/User/LdapUserProvider.php109
-rw-r--r--Core/composer.json6
8 files changed, 369 insertions, 52 deletions
diff --git a/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
new file mode 100644
index 0000000..fab7d80
--- /dev/null
+++ b/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
@@ -0,0 +1,85 @@
+<?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\Component\Security\Core\Authentication\Provider;
+
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Core\Exception\BadCredentialsException;
+use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+use Symfony\Component\Security\Core\User\UserCheckerInterface;
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\User\UserProviderInterface;
+use Symfony\Component\Ldap\LdapClientInterface;
+use Symfony\Component\Ldap\Exception\ConnectionException;
+
+/**
+ * LdapBindAuthenticationProvider authenticates a user against an LDAP server.
+ *
+ * The only way to check user credentials is to try to connect the user with its
+ * credentials to the ldap.
+ *
+ * @author Charles Sarrazin <charles@sarraz.in>
+ */
+class LdapBindAuthenticationProvider extends UserAuthenticationProvider
+{
+ private $userProvider;
+ private $ldap;
+ private $dnString;
+
+ /**
+ * Constructor.
+ *
+ * @param UserProviderInterface $userProvider A UserProvider
+ * @param UserCheckerInterface $userChecker A UserChecker
+ * @param string $providerKey The provider key
+ * @param LdapClientInterface $ldap An Ldap client
+ * @param string $dnString A string used to create the bind DN
+ * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not
+ */
+ public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, LdapClientInterface $ldap, $dnString = '{username}', $hideUserNotFoundExceptions = true)
+ {
+ parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
+
+ $this->userProvider = $userProvider;
+ $this->ldap = $ldap;
+ $this->dnString = $dnString;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function retrieveUser($username, UsernamePasswordToken $token)
+ {
+ if ('NONE_PROVIDED' === $username) {
+ throw new UsernameNotFoundException('Username can not be null');
+ }
+
+ return $this->userProvider->loadUserByUsername($username);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
+ {
+ $username = $token->getUsername();
+ $password = $token->getCredentials();
+
+ try {
+ $username = $this->ldap->escape($username, '', LdapClientInterface::LDAP_ESCAPE_DN);
+ $dn = str_replace('{username}', $username, $this->dnString);
+
+ $this->ldap->bind($dn, $password);
+ } catch (ConnectionException $e) {
+ throw new BadCredentialsException('The presented password is invalid.');
+ }
+ }
+}
diff --git a/Core/Resources/translations/security.tr.xlf b/Core/Resources/translations/security.tr.xlf
index fbf9b26..68c4421 100644
--- a/Core/Resources/translations/security.tr.xlf
+++ b/Core/Resources/translations/security.tr.xlf
@@ -8,7 +8,7 @@
</trans-unit>
<trans-unit id="2">
<source>Authentication credentials could not be found.</source>
- <target>Yetkilendirme girdileri bulunamadı.</target>
+ <target>Kimlik bilgileri bulunamadı.</target>
</trans-unit>
<trans-unit id="3">
<source>Authentication request could not be processed due to a system problem.</source>
@@ -16,7 +16,7 @@
</trans-unit>
<trans-unit id="4">
<source>Invalid credentials.</source>
- <target>Geçersiz girdiler.</target>
+ <target>Geçersiz kimlik bilgileri.</target>
</trans-unit>
<trans-unit id="5">
<source>Cookie has already been used by someone else.</source>
@@ -32,7 +32,7 @@
</trans-unit>
<trans-unit id="8">
<source>Digest nonce has expired.</source>
- <target>Derleme zaman aşımı gerçekleşti.</target>
+ <target>Derleme zaman aşımına uğradı.</target>
</trans-unit>
<trans-unit id="9">
<source>No authentication provider found to support the authentication token.</source>
@@ -44,7 +44,7 @@
</trans-unit>
<trans-unit id="11">
<source>No token could be found.</source>
- <target>Bilet bulunamadı.</target>
+ <target>Fiş bulunamadı.</target>
</trans-unit>
<trans-unit id="12">
<source>Username could not be found.</source>
@@ -56,11 +56,11 @@
</trans-unit>
<trans-unit id="14">
<source>Credentials have expired.</source>
- <target>Girdiler zaman aşımına uğradı.</target>
+ <target>Kimlik bilgileri zaman aşımına uğradı.</target>
</trans-unit>
<trans-unit id="15">
<source>Account is disabled.</source>
- <target>Hesap devre dışı bırakılmış.</target>
+ <target>Hesap engellenmiş.</target>
</trans-unit>
<trans-unit id="16">
<source>Account is locked.</source>
diff --git a/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
new file mode 100644
index 0000000..f1b5c03
--- /dev/null
+++ b/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
@@ -0,0 +1,61 @@
+<?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\Component\Security\Core\Tests\Authentication\Provider;
+
+use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
+use Symfony\Component\Security\Core\User\User;
+use Symfony\Component\Ldap\Exception\ConnectionException;
+
+class LdapBindAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
+ * @expectedExceptionMessage The presented password is invalid.
+ */
+ public function testBindFailureShouldThrowAnException()
+ {
+ $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface');
+ $ldap
+ ->expects($this->once())
+ ->method('bind')
+ ->will($this->throwException(new ConnectionException()))
+ ;
+ $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
+
+ $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap);
+ $reflection = new \ReflectionMethod($provider, 'checkAuthentication');
+ $reflection->setAccessible(true);
+
+ $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', '', 'key'));
+ }
+
+ public function testRetrieveUser()
+ {
+ $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
+ $userProvider
+ ->expects($this->once())
+ ->method('loadUserByUsername')
+ ->with('foo')
+ ;
+ $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface');
+
+ $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
+
+ $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap);
+ $reflection = new \ReflectionMethod($provider, 'retrieveUser');
+ $reflection->setAccessible(true);
+
+ $reflection->invoke($provider, 'foo', new UsernamePasswordToken('foo', 'bar', 'key'));
+ }
+}
diff --git a/Core/Tests/Authorization/Voter/AbstractVoterTest.php b/Core/Tests/Authorization/Voter/AbstractVoterTest.php
index 0fddd88..7062d39 100644
--- a/Core/Tests/Authorization/Voter/AbstractVoterTest.php
+++ b/Core/Tests/Authorization/Voter/AbstractVoterTest.php
@@ -95,7 +95,7 @@ class AbstractVoterTest_LegacyVoter extends AbstractVoter
{
protected function getSupportedClasses()
{
- return array('AbstractVoterTest_Object');
+ return array('stdClass');
}
protected function getSupportedAttributes()
@@ -113,7 +113,7 @@ class AbstractVoterTest_NothingImplementedVoter extends AbstractVoter
{
protected function getSupportedClasses()
{
- return array('AbstractVoterTest_Object');
+ return array('stdClass');
}
protected function getSupportedAttributes()
diff --git a/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php b/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php
deleted file mode 100644
index b49f23f..0000000
--- a/Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?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\Component\Security\Core\Tests\Authorization\Voter;
-
-use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
-
-class LegacyAbstractVoterTest_Voter extends AbstractVoter
-{
- protected function getSupportedClasses()
- {
- return array('AbstractVoterTest_Object');
- }
-
- protected function getSupportedAttributes()
- {
- return array('EDIT', 'CREATE');
- }
-
- protected function isGranted($attribute, $object, $user = null)
- {
- return 'EDIT' === $attribute;
- }
-}
-
-class LegacyAbstractVoterTest extends AbstractVoterTest
-{
- protected function setUp()
- {
- parent::setUp();
-
- $this->voter = new LegacyAbstractVoterTest_Voter();
- }
-}
diff --git a/Core/Tests/User/LdapUserProviderTest.php b/Core/Tests/User/LdapUserProviderTest.php
new file mode 100644
index 0000000..f56648b
--- /dev/null
+++ b/Core/Tests/User/LdapUserProviderTest.php
@@ -0,0 +1,102 @@
+<?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\Component\Security\Core\Tests\User;
+
+use Symfony\Component\Security\Core\User\LdapUserProvider;
+use Symfony\Component\Ldap\Exception\ConnectionException;
+
+class LdapUserProviderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
+ */
+ public function testLoadUserByUsernameFailsIfCantConnectToLdap()
+ {
+ $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface');
+ $ldap
+ ->expects($this->once())
+ ->method('bind')
+ ->will($this->throwException(new ConnectionException()))
+ ;
+
+ $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
+ $provider->loadUserByUsername('foo');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
+ */
+ public function testLoadUserByUsernameFailsIfNoLdapEntries()
+ {
+ $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface');
+ $ldap
+ ->expects($this->once())
+ ->method('escape')
+ ->will($this->returnValue('foo'))
+ ;
+
+ $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
+ $provider->loadUserByUsername('foo');
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException
+ */
+ public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry()
+ {
+ $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface');
+ $ldap
+ ->expects($this->once())
+ ->method('escape')
+ ->will($this->returnValue('foo'))
+ ;
+ $ldap
+ ->expects($this->once())
+ ->method('find')
+ ->will($this->returnValue(array(
+ array(),
+ array(),
+ 'count' => 2,
+ )))
+ ;
+
+ $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
+ $provider->loadUserByUsername('foo');
+ }
+
+ public function testSuccessfulLoadUserByUsername()
+ {
+ $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface');
+ $ldap
+ ->expects($this->once())
+ ->method('escape')
+ ->will($this->returnValue('foo'))
+ ;
+ $ldap
+ ->expects($this->once())
+ ->method('find')
+ ->will($this->returnValue(array(
+ array(
+ 'sAMAccountName' => 'foo',
+ 'userpassword' => 'bar',
+ ),
+ 'count' => 1,
+ )))
+ ;
+
+ $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com');
+ $this->assertInstanceOf(
+ 'Symfony\Component\Security\Core\User\User',
+ $provider->loadUserByUsername('foo')
+ );
+ }
+}
diff --git a/Core/User/LdapUserProvider.php b/Core/User/LdapUserProvider.php
new file mode 100644
index 0000000..ec699fc
--- /dev/null
+++ b/Core/User/LdapUserProvider.php
@@ -0,0 +1,109 @@
+<?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\Component\Security\Core\User;
+
+use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
+use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+use Symfony\Component\Ldap\Exception\ConnectionException;
+use Symfony\Component\Ldap\LdapClientInterface;
+
+/**
+ * LdapUserProvider is a simple user provider on top of ldap.
+ *
+ * @author Grégoire Pineau <lyrixx@lyrixx.info>
+ * @author Charles Sarrazin <charles@sarraz.in>
+ */
+class LdapUserProvider implements UserProviderInterface
+{
+ private $ldap;
+ private $baseDn;
+ private $searchDn;
+ private $searchPassword;
+ private $defaultRoles;
+ private $defaultSearch;
+
+ /**
+ * @param LdapClientInterface $ldap
+ * @param string $baseDn
+ * @param string $searchDn
+ * @param string $searchPassword
+ * @param array $defaultRoles
+ * @param string $uidKey
+ * @param string $filter
+ */
+ public function __construct(LdapClientInterface $ldap, $baseDn, $searchDn = null, $searchPassword = null, array $defaultRoles = array(), $uidKey = 'sAMAccountName', $filter = '({uid_key}={username})')
+ {
+ $this->ldap = $ldap;
+ $this->baseDn = $baseDn;
+ $this->searchDn = $searchDn;
+ $this->searchPassword = $searchPassword;
+ $this->defaultRoles = $defaultRoles;
+ $this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadUserByUsername($username)
+ {
+ try {
+ $this->ldap->bind($this->searchDn, $this->searchPassword);
+ $username = $this->ldap->escape($username, '', LdapClientInterface::LDAP_ESCAPE_FILTER);
+ $query = str_replace('{username}', $username, $this->defaultSearch);
+ $search = $this->ldap->find($this->baseDn, $query);
+ } catch (ConnectionException $e) {
+ throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e);
+ }
+
+ if (!$search) {
+ throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
+ }
+
+ if ($search['count'] > 1) {
+ throw new UsernameNotFoundException('More than one user found');
+ }
+
+ $user = $search[0];
+
+ return $this->loadUser($username, $user);
+ }
+
+ public function loadUser($username, $user)
+ {
+ $password = isset($user['userpassword']) ? $user['userpassword'] : null;
+
+ $roles = $this->defaultRoles;
+
+ return new User($username, $password, $roles);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function refreshUser(UserInterface $user)
+ {
+ if (!$user instanceof User) {
+ throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
+ }
+
+ return new User($user->getUsername(), null, $user->getRoles());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supportsClass($class)
+ {
+ return $class === 'Symfony\Component\Security\Core\User\User';
+ }
+
+}
diff --git a/Core/composer.json b/Core/composer.json
index 7671993..a7e9db7 100644
--- a/Core/composer.json
+++ b/Core/composer.json
@@ -25,13 +25,15 @@
"symfony/http-foundation": "~2.8|~3.0",
"symfony/translation": "~2.8|~3.0",
"symfony/validator": "~2.8|~3.0",
- "psr/log": "~1.0"
+ "psr/log": "~1.0",
+ "symfony/ldap": "~2.8|~3.0.0"
},
"suggest": {
"symfony/event-dispatcher": "",
"symfony/http-foundation": "",
"symfony/validator": "For using the user password constraint",
- "symfony/expression-language": "For using the expression voter"
+ "symfony/expression-language": "For using the expression voter",
+ "symfony/ldap": "For using LDAP integration"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Security\\Core\\": "" }