diff options
author | Tobias Schultze <webmaster@tubo-world.de> | 2015-09-29 16:08:28 +0200 |
---|---|---|
committer | Tobias Schultze <webmaster@tubo-world.de> | 2015-09-29 16:08:28 +0200 |
commit | 493704bf17328063a6e566d912e6e063d4c60f8b (patch) | |
tree | 16e3cfaffcb041445478540434fd4e5be0b3eccc /Core | |
parent | 16223cbf326eee2a9fff59f765c218ff028e9330 (diff) | |
parent | 889a989997c4b038fb4e354e57e35ede82370581 (diff) | |
download | symfony-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.php | 85 | ||||
-rw-r--r-- | Core/Resources/translations/security.tr.xlf | 12 | ||||
-rw-r--r-- | Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php | 61 | ||||
-rw-r--r-- | Core/Tests/Authorization/Voter/AbstractVoterTest.php | 4 | ||||
-rw-r--r-- | Core/Tests/Authorization/Voter/LegacyAbstractVoterTest.php | 42 | ||||
-rw-r--r-- | Core/Tests/User/LdapUserProviderTest.php | 102 | ||||
-rw-r--r-- | Core/User/LdapUserProvider.php | 109 | ||||
-rw-r--r-- | Core/composer.json | 6 |
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\\": "" } |