diff options
8 files changed, 113 insertions, 3 deletions
diff --git a/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/Core/Authentication/Provider/LdapBindAuthenticationProvider.php index 950b603..5ebb09a 100644 --- a/Core/Authentication/Provider/LdapBindAuthenticationProvider.php +++ b/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -73,6 +73,10 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider $username = $token->getUsername(); $password = $token->getCredentials(); + if ('' === $password) { + throw new BadCredentialsException('The presented password must not be empty.'); + } + try { $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN); $dn = str_replace('{username}', $username, $this->dnString); diff --git a/Core/Authentication/Token/AnonymousToken.php b/Core/Authentication/Token/AnonymousToken.php index e1dfef4..2c73cb4 100644 --- a/Core/Authentication/Token/AnonymousToken.php +++ b/Core/Authentication/Token/AnonymousToken.php @@ -26,7 +26,7 @@ class AnonymousToken extends AbstractToken * Constructor. * * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client - * @param string $user The user + * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string. * @param RoleInterface[] $roles An array of roles */ public function __construct($secret, $user, array $roles = array()) diff --git a/Core/Authentication/Token/PreAuthenticatedToken.php b/Core/Authentication/Token/PreAuthenticatedToken.php index 1798203..5a3fc95 100644 --- a/Core/Authentication/Token/PreAuthenticatedToken.php +++ b/Core/Authentication/Token/PreAuthenticatedToken.php @@ -26,7 +26,7 @@ class PreAuthenticatedToken extends AbstractToken /** * Constructor. * - * @param string|object $user The user + * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string. * @param mixed $credentials The user credentials * @param string $providerKey The provider key * @param RoleInterface[]|string[] $roles An array of roles diff --git a/Core/Security.php b/Core/Security.php index 14d32f8..84cc77d 100644 --- a/Core/Security.php +++ b/Core/Security.php @@ -21,4 +21,5 @@ final class Security const ACCESS_DENIED_ERROR = '_security.403_error'; const AUTHENTICATION_ERROR = '_security.last_error'; const LAST_USERNAME = '_security.last_username'; + const MAX_USERNAME_LENGTH = 4096; } diff --git a/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php index 4d2eead..da3068f 100644 --- a/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php +++ b/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php @@ -26,6 +26,23 @@ class LdapBindAuthenticationProviderTest extends \PHPUnit_Framework_TestCase { /** * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException + * @expectedExceptionMessage The presented password must not be empty. + */ + public function testEmptyPasswordShouldThrowAnException() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $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, 'checkAuthentication'); + $reflection->setAccessible(true); + + $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', '', 'key')); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException * @expectedExceptionMessage The presented password is invalid. */ public function testBindFailureShouldThrowAnException() @@ -43,7 +60,7 @@ class LdapBindAuthenticationProviderTest extends \PHPUnit_Framework_TestCase $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); $reflection->setAccessible(true); - $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', '', 'key')); + $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); } public function testRetrieveUser() diff --git a/Http/Firewall/SimpleFormAuthenticationListener.php b/Http/Firewall/SimpleFormAuthenticationListener.php index 76c66bc..7c940c3 100644 --- a/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/Http/Firewall/SimpleFormAuthenticationListener.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerI use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\ParameterBagUtils; @@ -107,6 +108,10 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } + if (strlen($username) > Security::MAX_USERNAME_LENGTH) { + throw new BadCredentialsException('Invalid username.'); + } + $request->getSession()->set(Security::LAST_USERNAME, $username); $token = $this->simpleAuthenticator->createToken($request, $username, $password, $this->providerKey); diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index c8195ce..426457d 100644 --- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -23,6 +23,7 @@ use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\Security; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -83,6 +84,10 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } + if (strlen($username) > Security::MAX_USERNAME_LENGTH) { + throw new BadCredentialsException('Invalid username.'); + } + $request->getSession()->set(Security::LAST_USERNAME, $username); return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey)); diff --git a/Tests/Http/Firewall/UsernamePasswordFormAuthenticationListenerTest.php b/Tests/Http/Firewall/UsernamePasswordFormAuthenticationListenerTest.php new file mode 100644 index 0000000..eca14d3 --- /dev/null +++ b/Tests/Http/Firewall/UsernamePasswordFormAuthenticationListenerTest.php @@ -0,0 +1,78 @@ +<?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\Tests\Http\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener; +use Symfony\Component\Security\Core\Security; + +class UsernamePasswordFormAuthenticationListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getUsernameForLength + */ + public function testHandleWhenUsernameLength($username, $ok) + { + $request = Request::create('/login_check', 'POST', array('_username' => $username)); + $request->setSession($this->getMock('Symfony\Component\HttpFoundation\Session\SessionInterface')); + + $httpUtils = $this->getMock('Symfony\Component\Security\Http\HttpUtils'); + $httpUtils + ->expects($this->any()) + ->method('checkRequestPath') + ->will($this->returnValue(true)) + ; + + $failureHandler = $this->getMock('Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface'); + $failureHandler + ->expects($ok ? $this->never() : $this->once()) + ->method('onAuthenticationFailure') + ->will($this->returnValue(new Response())) + ; + + $authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager')->disableOriginalConstructor()->getMock(); + $authenticationManager + ->expects($ok ? $this->once() : $this->never()) + ->method('authenticate') + ->will($this->returnValue(new Response())) + ; + + $listener = new UsernamePasswordFormAuthenticationListener( + $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'), + $authenticationManager, + $this->getMock('Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface'), + $httpUtils, + 'TheProviderKey', + $this->getMock('Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface'), + $failureHandler, + array('require_previous_session' => false) + ); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + public function getUsernameForLength() + { + return array( + array(str_repeat('x', Security::MAX_USERNAME_LENGTH + 1), false), + array(str_repeat('x', Security::MAX_USERNAME_LENGTH - 1), true), + ); + } +} |