diff --git a/CHANGELOG-2.3.md b/CHANGELOG-2.3.md index b8e185e9de783..d0ca6221aca02 100644 --- a/CHANGELOG-2.3.md +++ b/CHANGELOG-2.3.md @@ -7,6 +7,35 @@ in 2.3 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.3.0...v2.3.1 +* 2.3.40 (2016-04-29) + + * bug #18246 [DependencyInjection] fix ambiguous services schema (backbone87) + * bug #18603 [PropertyAccess] ->getValue() should be read-only (nicolas-grekas) + * bug #18280 [Routing] add query param if value is different from default (Tobion) + * bug #18515 [Filesystem] Better error handling in remove() (nicolas-grekas) + * bug #18449 [PropertyAccess] Fix regression (nicolas-grekas) + * bug #18467 [DependencyInjection] Resolve aliases before removing abstract services + add tests (nicolas-grekas) + * bug #18460 [DomCrawler] Fix select option with empty value (Matt Wells) + * bug #18425 [Security] Fixed SwitchUserListener when exiting an impersonation with AnonymousToken (lyrixx) + * bug #18317 [Form] fix "prototype" not required when parent form is not required (HeahDude) + * bug #18439 [Logging] Add support for Firefox (43+) in ChromePhpHandler (arjenm) + * bug #18385 Detect CLI color support for Windows 10 build 10586 (mlocati) + * bug #18426 [EventDispatcher] Try first if the event is Stopped (lyrixx) + * bug #18265 Optimize ReplaceAliasByActualDefinitionPass (ajb-in) + * bug #18358 [Form] NumberToLocalizedStringTransformer should return floats when possible (nicolas-grekas) + * bug #17926 [DependencyInjection] Enable alias for service_container (hason) + * bug #18336 [Debug] Fix handling of php7 throwables (nicolas-grekas) + * bug #18312 [ClassLoader] Fix storing not-found classes in APC cache (nicolas-grekas) + * bug #18255 [HttpFoundation] Fix support of custom mime types with parameters (Ener-Getick) + * bug #18259 [PropertyAccess] Backport fixes from 2.7 (nicolas-grekas) + * bug #18224 [PropertyAccess] Remove most ref mismatches to improve perf (nicolas-grekas) + * bug #18210 [PropertyAccess] Throw an UnexpectedTypeException when the type do not match (dunglas, nicolas-grekas) + * bug #18216 [Intl] Fix invalid numeric literal on PHP 7 (nicolas-grekas) + * bug #18147 [Validator] EmailValidator cannot extract hostname if email contains multiple @ symbols (natechicago) + * bug #18175 [Translation] Add support for fuzzy tags in PoFileLoader (nud) + * bug #18179 [Form] Fix NumberToLocalizedStringTransformer::reverseTransform with big integers (ovrflo, nicolas-grekas) + * bug #18164 [HttpKernel] set s-maxage only if all responses are cacheable (xabbuh) + * 2.3.39 (2016-03-13) * bug #18080 [HttpFoundation] Set the Content-Range header if the requested Range is unsatisfied (jakzal) diff --git a/CHANGELOG-2.7.md b/CHANGELOG-2.7.md index 89ee3b549f479..cfe286f2a4caf 100644 --- a/CHANGELOG-2.7.md +++ b/CHANGELOG-2.7.md @@ -7,6 +7,14 @@ in 2.7 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v2.7.0...v2.7.1 +* 2.7.13 (2016-05-09) + + * security #18733 limited the maximum length of a submitted username (fabpot) + * bug #18730 [FrameworkBundle] prevent calling get() for service_container service (xabbuh) + * bug #18709 [DependencyInjection] top-level anonymous services must be public (xabbuh) + * bug #18692 add @Event annotation for KernelEvents (Haehnchen) + * bug #18246 [DependencyInjection] fix ambiguous services schema (backbone87) + * 2.7.12 (2016-04-29) * bug #18180 [Form] fixed BC break with pre selection of choices with `ChoiceType` and its children (HeahDude) diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php index ae13d543b0e1e..3f3c577b846df 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php @@ -33,6 +33,8 @@ public function testCreateProxyServiceWithRuntimeInstantiator() $builder->register('foo1', 'ProxyManagerBridgeFooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); $builder->getDefinition('foo1')->setLazy(true); + $builder->compile(); + /* @var $foo1 \ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface */ $foo1 = $builder->get('foo1'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index d6e0c14da4893..33334e79920c9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -274,6 +274,10 @@ protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceI return $builder->getAlias($serviceId); } + if ('service_container' === $serviceId) { + return $builder; + } + // the service has been injected in some special way, just return the service return $builder->get($serviceId); } diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index f4ec265d533f7..9e2e679e61c95 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -292,6 +292,10 @@ private function processAnonymousServices(\DOMDocument $xml, $file) if ($services = $this->getChildren($node, 'service')) { $definitions[$id] = array($services[0], $file, false); $services[0]->setAttribute('id', $id); + + // anonymous services are always private + // we could not use the constant false here, because of XML parsing + $services[0]->setAttribute('public', 'false'); } } } @@ -311,9 +315,6 @@ private function processAnonymousServices(\DOMDocument $xml, $file) foreach ($definitions as $id => $def) { list($domElement, $file, $wild) = $def; - // anonymous services are always private - // we could not use the constant false here, because of XML parsing - $domElement->setAttribute('public', 'false'); if (null !== $definition = $this->parseDefinition($domElement, $file)) { $this->container->setDefinition($id, $definition); diff --git a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd index c8e747efffbc3..a7a534c9f1b64 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd +++ b/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -24,14 +24,28 @@ ]]> - - - - - + + + + + + + + + + + + + + + + + + + getDefinition($id); } $this->assertEquals('BizClass', $service->getClass(), '->load() uses the same configuration as for the anonymous ones'); - $this->assertFalse($service->isPublic()); + $this->assertTrue($service->isPublic()); // anonymous services are shared when using decoration definitions $container->compile(); diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index c2b2550607005..d42c7a0c6c0d6 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -29,6 +29,7 @@ "suggest": { "symfony/yaml": "", "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" }, "autoload": { diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index bee3360413739..c348cac299911 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,11 +58,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.12'; - const VERSION_ID = 20712; + const VERSION = '2.7.13'; + const VERSION_ID = 20713; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; - const RELEASE_VERSION = 12; + const RELEASE_VERSION = 13; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '05/2018'; diff --git a/src/Symfony/Component/HttpKernel/KernelEvents.php b/src/Symfony/Component/HttpKernel/KernelEvents.php index 8768979da6bd4..abbbfcc0048b9 100644 --- a/src/Symfony/Component/HttpKernel/KernelEvents.php +++ b/src/Symfony/Component/HttpKernel/KernelEvents.php @@ -108,6 +108,10 @@ final class KernelEvents * * This event allows you to reset the global and environmental state of * the application, when it was changed during the request. + * The event listener method receives a + * Symfony\Component\HttpKernel\Event\FinishRequestEvent instance. + * + * @Event * * @var string */ diff --git a/src/Symfony/Component/OptionsResolver/README.md b/src/Symfony/Component/OptionsResolver/README.md index 5768f68e7ff7d..245e69b548d6d 100644 --- a/src/Symfony/Component/OptionsResolver/README.md +++ b/src/Symfony/Component/OptionsResolver/README.md @@ -1,7 +1,7 @@ OptionsResolver Component ========================= -The OptionsResolver component is `array_replace on steroids. It allows you to +The OptionsResolver component is `array_replace` on steroids. It allows you to create an options system with required options, defaults, validation (type, value), normalization and more. diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php index 571816ca9ecb2..5f3241f20d62f 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AnonymousToken.php @@ -26,7 +26,7 @@ class AnonymousToken extends AbstractToken * Constructor. * * @param string $key The key shared with the authentication provider - * @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($key, $user, array $roles = array()) diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php index 1798203690455..5a3fc95327c08 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticatedToken.php +++ b/src/Symfony/Component/Security/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/src/Symfony/Component/Security/Core/AuthenticationEvents.php b/src/Symfony/Component/Security/Core/AuthenticationEvents.php index 90b714267466c..13bce30768908 100644 --- a/src/Symfony/Component/Security/Core/AuthenticationEvents.php +++ b/src/Symfony/Component/Security/Core/AuthenticationEvents.php @@ -20,6 +20,8 @@ final class AuthenticationEvents * The event listener method receives a * Symfony\Component\Security\Core\Event\AuthenticationEvent instance. * + * @Event + * * @var string */ const AUTHENTICATION_SUCCESS = 'security.authentication.success'; @@ -32,6 +34,8 @@ final class AuthenticationEvents * Symfony\Component\Security\Core\Event\AuthenticationFailureEvent * instance. * + * @Event + * * @var string */ const AUTHENTICATION_FAILURE = 'security.authentication.failure'; diff --git a/src/Symfony/Component/Security/Core/Security.php b/src/Symfony/Component/Security/Core/Security.php index 14d32f81482e5..84cc77dcf7f32 100644 --- a/src/Symfony/Component/Security/Core/Security.php +++ b/src/Symfony/Component/Security/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/src/Symfony/Component/Security/Core/SecurityContextInterface.php b/src/Symfony/Component/Security/Core/SecurityContextInterface.php index 2a06ca426a487..73edd2383573b 100644 --- a/src/Symfony/Component/Security/Core/SecurityContextInterface.php +++ b/src/Symfony/Component/Security/Core/SecurityContextInterface.php @@ -26,4 +26,5 @@ interface SecurityContextInterface extends TokenStorageInterface, AuthorizationC const ACCESS_DENIED_ERROR = Security::ACCESS_DENIED_ERROR; const AUTHENTICATION_ERROR = Security::AUTHENTICATION_ERROR; const LAST_USERNAME = Security::LAST_USERNAME; + const MAX_USERNAME_LENGTH = Security::MAX_USERNAME_LENGTH; } diff --git a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php index 4733b6ac261d5..8123e0e22e7b5 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php @@ -24,6 +24,7 @@ use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\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\Session\SessionAuthenticationStrategyInterface; @@ -116,6 +117,10 @@ protected function attemptAuthentication(Request $request) $password = $request->get($this->options['password_parameter'], null, true); } + 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/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index 07ab85ae7e8fa..ba4329b0eadc4 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -24,6 +24,7 @@ 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\InvalidArgumentException; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\Security; @@ -91,6 +92,10 @@ protected function attemptAuthentication(Request $request) $password = $request->get($this->options['password_parameter'], null, true); } + 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/src/Symfony/Component/Security/Tests/Http/Firewall/UsernamePasswordFormAuthenticationListenerTest.php b/src/Symfony/Component/Security/Tests/Http/Firewall/UsernamePasswordFormAuthenticationListenerTest.php new file mode 100644 index 0000000000000..b7c6ab9db5752 --- /dev/null +++ b/src/Symfony/Component/Security/Tests/Http/Firewall/UsernamePasswordFormAuthenticationListenerTest.php @@ -0,0 +1,78 @@ + + * + * 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\SecurityContextInterface; + +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\SecurityContextInterface'), + $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', SecurityContextInterface::MAX_USERNAME_LENGTH + 1), false), + array(str_repeat('x', SecurityContextInterface::MAX_USERNAME_LENGTH - 1), true), + ); + } +}