diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 94f0fabcc4676..b6f39741d9dbc 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
| Q | A
| ------------- | ---
-| Branch? | master for features / 2.7 up to 4.0 for bug fixes
+| Branch? | master for features / 2.8 up to 4.1 for bug fixes
| Bug fix? | yes/no
| New feature? | yes/no
| BC breaks? | no
diff --git a/CHANGELOG-4.0.md b/CHANGELOG-4.0.md
index 9abb4a61a6634..7131036ba4fa8 100644
--- a/CHANGELOG-4.0.md
+++ b/CHANGELOG-4.0.md
@@ -7,6 +7,47 @@ in 4.0 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/v4.0.0...v4.0.1
+* 4.0.11 (2018-05-25)
+
+ * bug #27364 [DI] Fix bad exception on uninitialized references to non-shared services (nicolas-grekas)
+ * bug #27359 [HttpFoundation] Fix perf issue during MimeTypeGuesser intialization (nicolas-grekas)
+ * security #cve-2018-11408 [SecurityBundle] Fail if security.http_utils cannot be configured
+ * security #cve-2018-11406 clear CSRF tokens when the user is logged out
+ * security #cve-2018-11385 migrating session for UsernamePasswordJsonAuthenticationListener
+ * security #cve-2018-11385 Adding session authentication strategy to Guard to avoid session fixation
+ * security #cve-2018-11385 Adding session strategy to ALL listeners to avoid *any* possible fixation
+ * security #cve-2018-11386 [HttpFoundation] Break infinite loop in PdoSessionHandler when MySQL is in loose mode
+ * bug #27341 [WebProfilerBundle] Fixed validator/dump trace CSS (yceruto)
+ * bug #27337 [FrameworkBundle] fix typo in CacheClearCommand (emilielorenzo)
+
+* 4.0.10 (2018-05-21)
+
+ * bug #27264 [Validator] Use strict type in URL validator (mimol91)
+ * bug #27267 [DependencyInjection] resolve array env vars (jamesthomasonjr)
+ * bug #26781 [Form] Fix precision of MoneyToLocalizedStringTransformer's divisions on transform() (syastrebov)
+ * bug #27286 [Translation] Add Occitan plural rule (kylekatarnls)
+ * bug #27271 [DI] Allow defining bindings on ChildDefinition (nicolas-grekas)
+ * bug #27246 Disallow invalid characters in session.name (ostrolucky)
+ * bug #27287 [PropertyInfo] fix resolving parent|self type hints (nicolas-grekas)
+ * bug #27281 [HttpKernel] Fix dealing with self/parent in ArgumentMetadataFactory (fabpot)
+ * bug #24805 [Security] Fix logout (MatTheCat)
+ * bug #27265 [DI] Shared services should not be inlined in non-shared ones (nicolas-grekas)
+ * bug #27141 [Process] Suppress warnings when open_basedir is non-empty (cbj4074)
+ * bug #27250 [Session] limiting :key for GET_LOCK to 64 chars (oleg-andreyev)
+ * bug #27237 [Debug] Fix populating error_get_last() for handled silent errors (nicolas-grekas)
+ * bug #27232 [Cache][Lock] Fix usages of error_get_last() (nicolas-grekas)
+ * bug #27236 [Filesystem] Fix usages of error_get_last() (nicolas-grekas)
+ * bug #27191 [DI] Display previous error messages when throwing unused bindings (nicolas-grekas)
+ * bug #27231 [FrameworkBundle] Fix cache:clear on vagrant (nicolas-grekas)
+ * bug #27222 [WebProfilerBundle][Cache] Fix misses calculation when calling getItems (fsevestre)
+ * bug #27227 [HttpKernel] Handle NoConfigurationException "onKernelException()" (nicolas-grekas)
+ * bug #27152 [HttpFoundation] use brace-style regex delimiters (xabbuh)
+ * bug #27158 [Cache] fix logic for fetching tag versions on TagAwareAdapter (dmaicher)
+ * bug #27143 [Console] By default hide the short exception trace line from exception messages in Symfony's commands (yceruto)
+ * bug #27133 [Doctrine Bridge] fix priority for doctrine event listeners (dmaicher)
+ * bug #27135 [FrameworkBundle] Use the correct service id for CachePoolPruneCommand in its compiler pass (DemonTPx)
+ * feature #24896 Add CODE_OF_CONDUCT.md (egircys)
+
* 4.0.9 (2018-04-30)
* bug #27074 [Debug][WebProfilerBundle] Fix setting file link format (lyrixx, nicolas-grekas)
diff --git a/CHANGELOG-4.1.md b/CHANGELOG-4.1.md
index 5437c76fd6bad..a042563276456 100644
--- a/CHANGELOG-4.1.md
+++ b/CHANGELOG-4.1.md
@@ -7,6 +7,30 @@ in 4.1 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/v4.1.0...v4.1.1
+* 4.1.0-BETA3 (2018-05-26)
+
+ * bug #27388 [Routing] Account for greediness when merging route patterns (nicolas-grekas)
+ * bug #27344 [HttpKernel] reset kernel start time on reboot (kiler129)
+ * bug #27365 [Serializer] Check the value of enable_max_depth if defined (dunglas)
+ * bug #27358 [PhpUnitBridge] silence some stderr outputs (ostrolucky)
+ * bug #27366 [DI] never inline lazy services (nicolas-grekas)
+ * bug #27352 Remove reference to the test container after kernel shutdown (stof)
+ * bug #27350 [HttpKernel] fix deprecation in AbstractTestSessionListener (alekitto)
+ * bug #27367 [FrameworkBundle] cleanup generated test container (nicolas-grekas)
+ * bug #27379 [FrameworkBundle] Fix using test.service_container when Client is rebooted (nicolas-grekas)
+ * bug #27364 [DI] Fix bad exception on uninitialized references to non-shared services (nicolas-grekas)
+ * bug #27359 [HttpFoundation] Fix perf issue during MimeTypeGuesser intialization (nicolas-grekas)
+ * security #cve-2018-11408 [SecurityBundle] Fail if security.http_utils cannot be configured
+ * security #cve-2018-11406 clear CSRF tokens when the user is logged out
+ * security #cve-2018-11385 migrating session for UsernamePasswordJsonAuthenticationListener
+ * security #cve-2018-11385 migrating session for UsernamePasswordJsonAuthenticationListener
+ * security #cve-2018-11385 Adding session authentication strategy to Guard to avoid session fixation
+ * security #cve-2018-11385 Adding session strategy to ALL listeners to avoid *any* possible fixation
+ * security #cve-2018-11386 [HttpFoundation] Break infinite loop in PdoSessionHandler when MySQL is in loose mode
+ * bug #27341 [WebProfilerBundle] Fixed validator/dump trace CSS (yceruto)
+ * bug #27337 [FrameworkBundle] fix typo in CacheClearCommand (emilielorenzo)
+ * bug #27292 [Serializer] Fix and improve constraintViolationListNormalizer's RFC7807 compliance (dunglas)
+
* 4.1.0-BETA2 (2018-05-21)
* bug #27312 Supress deprecation notices thrown when getting private servies from container in tests (arderyp)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index bc4ebf6f78139..672246e7f9da8 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -17,9 +17,9 @@ Symfony is the result of the work of many people who made the code better
- Johannes S (johannes)
- Jakub Zalas (jakubzalas)
- Kris Wallsmith (kriswallsmith)
+ - Maxime Steinhausser (ogizanagi)
- Ryan Weaver (weaverryan)
- Javier Eguiluz (javier.eguiluz)
- - Maxime Steinhausser (ogizanagi)
- Grégoire Pineau (lyrixx)
- Hugo Hamon (hhamon)
- Abdellatif Ait boudad (aitboudad)
@@ -35,17 +35,17 @@ Symfony is the result of the work of many people who made the code better
- Jean-François Simon (jfsimon)
- Benjamin Eberlei (beberlei)
- Igor Wiedler (igorw)
+ - Samuel ROZE (sroze)
- Jules Pietri (heah)
- Eriksen Costa (eriksencosta)
- Guilhem Niot (energetick)
- Sarah Khalil (saro0h)
- - Samuel ROZE (sroze)
+ - Yonel Ceruto (yonelceruto)
- Jonathan Wage (jwage)
- Hamza Amrouche (simperfit)
- Diego Saint Esteben (dosten)
- - Yonel Ceruto (yonelceruto)
- - Alexandre Salomé (alexandresalome)
- Iltar van der Berg (kjarli)
+ - Alexandre Salomé (alexandresalome)
- William Durand (couac)
- ornicar
- Francis Besset (francisbesset)
@@ -59,9 +59,9 @@ Symfony is the result of the work of many people who made the code better
- Henrik Bjørnskov (henrikbjorn)
- Dany Maillard (maidmaid)
- Miha Vrhovnik
+ - Kevin Bond (kbond)
- Tobias Nyholm (tobias)
- Diego Saint Esteben (dii3g0)
- - Kevin Bond (kbond)
- Konstantin Kudryashov (everzet)
- Alexander M. Turek (derrabus)
- Bilal Amarni (bamarni)
@@ -83,12 +83,12 @@ Symfony is the result of the work of many people who made the code better
- Dariusz Górecki (canni)
- Issei Murasawa (issei_m)
- Douglas Greenshields (shieldo)
+ - David Maicher (dmaicher)
- Lee McDermott
- Brandon Turner
- Luis Cordova (cordoval)
- Graham Campbell (graham)
- Daniel Holmes (dholmes)
- - David Maicher (dmaicher)
- Dariusz Ruminski
- Toni Uebernickel (havvg)
- Bart van den Burg (burgov)
@@ -103,9 +103,9 @@ Symfony is the result of the work of many people who made the code better
- Maxime STEINHAUSSER
- Michal Piotrowski (eventhorizon)
- Tim Nagel (merk)
+ - Grégoire Paris (greg0ire)
- Brice BERNARD (brikou)
- Baptiste Clavié (talus)
- - Grégoire Paris (greg0ire)
- marc.weistroff
- lenar
- Alexander Schwenn (xelaris)
@@ -139,21 +139,21 @@ Symfony is the result of the work of many people who made the code better
- Sebastian Hörl (blogsh)
- Daniel Gomes (danielcsgomes)
- Hidenori Goto (hidenorigoto)
+ - Jérôme Vasseur (jvasseur)
+ - Valentin Udaltsov (vudaltsov)
+ - gadelat (gadelat)
- Guilherme Blanco (guilhermeblanco)
- Pablo Godel (pgodel)
- - Jérôme Vasseur (jvasseur)
- Jérémie Augustin (jaugustin)
- Andréia Bohner (andreia)
- Philipp Wahala (hifi)
- Julien Falque (julienfalque)
- Rafael Dohms (rdohms)
- Arnaud Kleinpeter (nanocom)
- - gadelat (gadelat)
- jwdeitch
- Teoh Han Hui (teohhanhui)
- Mikael Pajunen
- Joel Wurtz (brouznouf)
- - Valentin Udaltsov (vudaltsov)
- Chris Wilkinson (thewilkybarkid)
- Oleg Voronkovich
- Vyacheslav Pavlov
@@ -226,6 +226,7 @@ Symfony is the result of the work of many people who made the code better
- Julien Brochet (mewt)
- Leo Feyer
- Tristan Darricau (nicofuma)
+ - Nikolay Labinskiy (e-moe)
- Michaël Perrin (michael.perrin)
- Marcel Beerta (mazen)
- Loïc Faugeron
@@ -260,6 +261,7 @@ Symfony is the result of the work of many people who made the code better
- Kristen Gilden (kgilden)
- Pierre-Yves LEBECQ (pylebecq)
- Jordan Samouh (jordansamouh)
+ - Baptiste Lafontaine (magnetik)
- Jakub Kucharovic (jkucharovic)
- Uwe Jäger (uwej711)
- Eugene Leonovich (rybakit)
@@ -270,7 +272,6 @@ Symfony is the result of the work of many people who made the code better
- Jan Sorgalla (jsor)
- Ray
- Tyson Andre
- - Nikolay Labinskiy (e-moe)
- Chekote
- Thomas Adam
- Albert Casademont (acasademont)
@@ -286,6 +287,7 @@ Symfony is the result of the work of many people who made the code better
- Oskar Stark (oskarstark)
- Thomas Lallement (raziel057)
- Giorgio Premi
+ - Christian Schmidt
- Beau Simensen (simensen)
- Michael Hirschler (mvhirsch)
- Robert Kiss (kepten)
@@ -317,7 +319,6 @@ Symfony is the result of the work of many people who made the code better
- Jerzy Zawadzki (jzawadzki)
- Wouter J
- Ismael Ambrosi (iambrosi)
- - Baptiste Lafontaine
- François Pluchino (francoispluchino)
- Aurelijus Valeiša (aurelijus)
- Jan Decavele (jandc)
@@ -361,6 +362,7 @@ Symfony is the result of the work of many people who made the code better
- Yaroslav Kiliba
- Terje Bråten
- Mathieu Lechat
+ - MatTheCat
- Robbert Klarenbeek (robbertkl)
- JhonnyL
- David Badura (davidbadura)
@@ -424,7 +426,6 @@ Symfony is the result of the work of many people who made the code better
- Jeanmonod David (jeanmonod)
- Christopher Davis (chrisguitarguy)
- Jan Schumann
- - Christian Schmidt
- Niklas Fiekas
- Markus Bachmann (baachi)
- lancergr
@@ -437,6 +438,7 @@ Symfony is the result of the work of many people who made the code better
- Josip Kruslin
- Asmir Mustafic (goetas)
- vagrant
+ - Aurimas Niekis (gcds)
- EdgarPE
- Florian Pfitzer (marmelatze)
- Asier Illarramendi (doup)
@@ -514,6 +516,7 @@ Symfony is the result of the work of many people who made the code better
- De Cock Xavier (xdecock)
- Almog Baku (almogbaku)
- Scott Arciszewski
+ - Xavier HAUSHERR
- Norbert Orzechowicz (norzechowicz)
- Denis Charrier (brucewouaigne)
- Matthijs van den Bos (matthijs)
@@ -529,7 +532,6 @@ Symfony is the result of the work of many people who made the code better
- Dawid Pakuła (zulusx)
- Florian Rey (nervo)
- Rodrigo Borrego Bernabé (rodrigobb)
- - MatTheCat
- Denis Gorbachev (starfall)
- Peter van Dommelen
- Tim van Densen
@@ -563,6 +565,7 @@ Symfony is the result of the work of many people who made the code better
- Mantas Var (mvar)
- Sebastian Krebs
- Jean-Christophe Cuvelier [Artack]
+ - Simon DELICATA
- alcaeus
- Fred Cox
- vitaliytv
@@ -580,6 +583,7 @@ Symfony is the result of the work of many people who made the code better
- James Johnston
- Sinan Eldem
- Alexandre Dupuy (satchette)
+ - Malte Blättermann
- Andre Rømcke (andrerom)
- Nahuel Cuesta (ncuesta)
- Chris Boden (cboden)
@@ -604,7 +608,6 @@ Symfony is the result of the work of many people who made the code better
- Michal Trojanowski
- David Fuhr
- Kamil Kokot (pamil)
- - Aurimas Niekis (gcds)
- Max Grigorian (maxakawizard)
- mcfedr (mcfedr)
- Rostyslav Kinash
@@ -721,6 +724,7 @@ Symfony is the result of the work of many people who made the code better
- Adam Szaraniec (mimol)
- Yosmany Garcia (yosmanyga)
- Wouter de Wild
+ - Antoine M (amakdessi)
- Degory Valentine
- izzyp
- Benoit Lévêque (benoit_leveque)
@@ -730,6 +734,7 @@ Symfony is the result of the work of many people who made the code better
- Xavier Lacot (xavier)
- possum
- Denis Zunke (donalberto)
+ - Philipp Cordes
- Ahmed TAILOULOUTE (ahmedtai)
- Olivier Maisonneuve (olineuve)
- Masterklavi
@@ -749,7 +754,6 @@ Symfony is the result of the work of many people who made the code better
- Adrien Lucas (adrienlucas)
- Zhuravlev Alexander (scif)
- James Michael DuPont
- - Xavier HAUSHERR
- Tom Klingenberg
- Christopher Hall (mythmakr)
- Patrick Dawkins (pjcdawkins)
@@ -802,6 +806,7 @@ Symfony is the result of the work of many people who made the code better
- corphi
- grizlik
- Derek ROTH
+ - Ben Johnson
- Dmytro Boiko (eagle)
- Shin Ohno (ganchiku)
- Geert De Deckere (geertdd)
@@ -882,6 +887,7 @@ Symfony is the result of the work of many people who made the code better
- Michael Tibben
- Billie Thompson
- Sander Marechal
+ - Icode4Food (icode4food)
- Radosław Benkel
- jean pasqualini (darkilliant)
- Ross Motley (rossmotley)
@@ -993,6 +999,7 @@ Symfony is the result of the work of many people who made the code better
- DerManoMann
- Olaf Klischat
- orlovv
+ - Jonathan Hedstrom
- Peter Smeets (darkspartan)
- Jhonny Lidfors (jhonny)
- Julien Bianchi (jubianchi)
@@ -1005,7 +1012,6 @@ Symfony is the result of the work of many people who made the code better
- Andrew Tch
- Alexander Cheprasov
- Rodrigo Díez Villamuera (rodrigodiez)
- - Malte Blättermann
- e-ivanov
- Jochen Bayer (jocl)
- Alex Bowers
@@ -1092,9 +1098,11 @@ Symfony is the result of the work of many people who made the code better
- Tobias Stöckler
- Mario Young
- Ilia (aliance)
+ - Chris McCafferty (cilefen)
- Grégoire Penverne (gpenverne)
- Mo Di (modi)
- Pablo Schläpfer
+ - Gert de Pagter
- Jelte Steijaert (jelte)
- Quique Porta (quiqueporta)
- stoccc
@@ -1177,9 +1185,9 @@ Symfony is the result of the work of many people who made the code better
- Andreas Frömer
- Philip Frank
- Lance McNearney
- - Antoine M (amakdessi)
- Gonzalo Vilaseca (gonzalovilaseca)
- Giorgio Premi
+ - ncou
- Ian Carroll
- caponica
- Matt Daum (daum)
@@ -1197,7 +1205,6 @@ Symfony is the result of the work of many people who made the code better
- Tadcka
- Beth Binkovitz
- Gonzalo Míguez
- - Philipp Cordes
- Pierre Rineau
- Romain Geissler
- Adrien Moiruad
@@ -1352,6 +1359,7 @@ Symfony is the result of the work of many people who made the code better
- Pablo Maria Martelletti (pmartelletti)
- Yassine Guedidi (yguedidi)
- Waqas Ahmed
+ - Bert Hekman
- Luis Muñoz
- Matthew Donadio
- Houziaux mike
@@ -1450,6 +1458,7 @@ Symfony is the result of the work of many people who made the code better
- Yannick Warnier (ywarnier)
- Kevin Decherf
- Jason Woods
+ - Oleg Andreyev
- klemens
- dened
- Dmitry Korotovsky
@@ -1508,6 +1517,7 @@ Symfony is the result of the work of many people who made the code better
- Pierre Rineau
- Maxim Lovchikov
- adenkejawen
+ - Florent SEVESTRE (aniki-taicho)
- Ari Pringle (apringle)
- Dan Ordille (dordille)
- Jan Eichhorn (exeu)
@@ -1759,7 +1769,6 @@ Symfony is the result of the work of many people who made the code better
- Matt Janssen
- Ben Miller
- Peter Gribanov
- - Ben Johnson
- kwiateusz
- David Soria Parra
- Sergiy Sokolenko
@@ -1889,6 +1898,7 @@ Symfony is the result of the work of many people who made the code better
- Julien Sanchez (sumbobyboys)
- Guillermo Gisinger (t3chn0r)
- Markus Tacker (tacker)
+ - Tarmo Leppänen (tarlepp)
- Tyler Stroud (tystr)
- Moritz Kraft (userfriendly)
- Víctor Mateo (victormateo)
diff --git a/README.md b/README.md
index 9db99a74c0cc0..5796b1acd7ceb 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ Community
* [Join the Symfony Community][11] and meet other members at the [Symfony events][12].
* [Get Symfony support][13] on Stack Overflow, Slack, IRC, etc.
* Follow us on [GitHub][14], [Twitter][15] and [Facebook][16].
+* Read our [Code of Conduct][24] and meet the [CARE Team][25]
Contributing
------------
@@ -71,3 +72,5 @@ Symfony development is sponsored by [SensioLabs][21], led by the
[21]: https://sensiolabs.com
[22]: https://symfony.com/doc/current/contributing/code/core_team.html
[23]: https://github.com/symfony/symfony-demo
+[24]: https://symfony.com/coc
+[25]: https://symfony.com/doc/current/contributing/code_of_conduct/care_team.html
diff --git a/phpunit b/phpunit
index c0ffe8ddef9e9..f4b80ed064121 100755
--- a/phpunit
+++ b/phpunit
@@ -8,7 +8,7 @@ if (!file_exists(__DIR__.'/vendor/symfony/phpunit-bridge/bin/simple-phpunit')) {
exit(1);
}
if (\PHP_VERSION_ID >= 70000 && !getenv('SYMFONY_PHPUNIT_VERSION')) {
- putenv('SYMFONY_PHPUNIT_VERSION=6.0');
+ putenv('SYMFONY_PHPUNIT_VERSION=6.5');
}
putenv('SYMFONY_PHPUNIT_DIR='.__DIR__.'/.phpunit');
require __DIR__.'/vendor/symfony/phpunit-bridge/bin/simple-phpunit';
diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
index f614ac99001b7..29b1960798b8c 100644
--- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
+++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php
@@ -109,30 +109,6 @@ public static function register($mode = 0)
}
$trace = debug_backtrace(true);
-
- // Silence deprecation warnings about private service accessed
- // from the service container if done so from a Test class.
- // As of Symfony 4.1, there is a new TestContainer that allows
- // fetching of private services within tests, so we no longer
- // need to warn about this behavior.
- //
- // NOTE: the event at the top of the stack $trace (index 0) should
- // always be the PhpUnitBridge's DeprecationErrorHandler; the
- // second event (index 1) should be the trigger_error() event;
- // the third event (index 2) should be the actual source of the
- // triggered deprecation notice; and the fourth event (index 3)
- // represents the action that called the deprecated code. In the
- // scenario that we want to suppress, the 4th event will be an
- // object instance of \PHPUnit\Framework\TestCase.
- if (isset($trace[3]['object'])) {
- $isPrivateServiceNotice = false !== strpos($msg, ' service is private, ');
- $isNoticeForContainerGetHasUsage = 'Symfony\Component\DependencyInjection\Container' === $trace[2]['class'] && in_array($trace[2]['function'], array('get', 'has'));
- $noticeWasTriggeredByPhpUnitTest = $trace[3]['object'] instanceof \PHPUnit\Framework\TestCase;
- if ($isPrivateServiceNotice && $isNoticeForContainerGetHasUsage && $noticeWasTriggeredByPhpUnitTest) {
- return false;
- }
- }
-
$group = 'other';
$isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $inVendors($file);
diff --git a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php
index 008ba437d83a9..134eefe940180 100644
--- a/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php
+++ b/src/Symfony/Bridge/PhpUnit/Tests/CoverageListenerTest.php
@@ -27,11 +27,11 @@ public function test()
$dir = __DIR__.'/../Tests/Fixtures/coverage';
$phpunit = $_SERVER['argv'][0];
- exec("$php $phpunit -c $dir/phpunit-without-listener.xml.dist $dir/tests/ --coverage-text", $output);
+ exec("$php $phpunit -c $dir/phpunit-without-listener.xml.dist $dir/tests/ --coverage-text 2> /dev/null", $output);
$output = implode("\n", $output);
$this->assertContains('FooCov', $output);
- exec("$php $phpunit -c $dir/phpunit-with-listener.xml.dist $dir/tests/ --coverage-text", $output);
+ exec("$php $phpunit -c $dir/phpunit-with-listener.xml.dist $dir/tests/ --coverage-text 2> /dev/null", $output);
$output = implode("\n", $output);
$this->assertNotContains('FooCov', $output);
$this->assertContains("SutNotFoundTest::test\nCould not find the tested class.", $output);
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit
index 670a152a7c3bb..00ab83000a26f 100755
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit
@@ -77,7 +77,7 @@ if ('phpdbg' === PHP_SAPI) {
$PHP .= ' -qrr';
}
-$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar') || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar`))
+$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar') || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`))
? $PHP.' '.escapeshellarg($COMPOSER)
: 'composer';
diff --git a/src/Symfony/Bundle/FrameworkBundle/Client.php b/src/Symfony/Bundle/FrameworkBundle/Client.php
index 1499d050370aa..a5f6a1500bfbf 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Client.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Client.php
@@ -30,15 +30,15 @@ class Client extends BaseClient
private $hasPerformedRequest = false;
private $profiler = false;
private $reboot = true;
- private $container;
+ private $testContainerId;
/**
* {@inheritdoc}
*/
- public function __construct(KernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null, ContainerInterface $container = null)
+ public function __construct(KernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null, string $testContainerId = null)
{
parent::__construct($kernel, $server, $history, $cookieJar);
- $this->container = $container;
+ $this->testContainerId = $testContainerId;
}
/**
@@ -48,7 +48,9 @@ public function __construct(KernelInterface $kernel, array $server = array(), Hi
*/
public function getContainer()
{
- return $this->container ?? $this->kernel->getContainer();
+ $container = $this->kernel->getContainer();
+
+ return null !== $this->testContainerId ? $container->get($this->testContainerId) : $container;
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
index ee4edf31720ad..2127d5fbe94fa 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
@@ -137,7 +137,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
if ('/' === \DIRECTORY_SEPARATOR && $mounts = @file('/proc/mounts')) {
foreach ($mounts as $mount) {
$mount = array_slice(explode(' ', $mount), 1, -3);
- if (!\in_array(array_pop($mount), array('vboxfs', 'nfs'))) {
+ if (!\in_array(array_pop($mount), array('vboxsf', 'nfs'))) {
continue;
}
$mount = implode(' ', $mount).'/';
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php
index 9e36a80d00ce6..19b36e3d2c843 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php
@@ -27,13 +27,21 @@ public function process(ContainerBuilder $container)
}
$testContainer = $container->getDefinition('test.service_container');
- $privateContainer = $container->getDefinition((string) $testContainer->getArgument(2));
+ $privateContainer = $testContainer->getArgument(2);
+ if ($privateContainer instanceof Reference) {
+ $privateContainer = $container->getDefinition((string) $privateContainer);
+ }
$definitions = $container->getDefinitions();
+ $privateServices = $privateContainer->getArgument(0);
- foreach ($privateContainer->getArgument(0) as $id => $argument) {
+ foreach ($privateServices as $id => $argument) {
if (isset($definitions[$target = (string) $argument->getValues()[0]])) {
$argument->setValues(array(new Reference($target)));
+ } else {
+ unset($privateServices[$id]);
}
}
+
+ $privateContainer->replaceArgument(0, $privateServices);
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php
index 62f17d64f1449..51fe553e19b2b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerWeakRefPass.php
@@ -31,7 +31,7 @@ public function process(ContainerBuilder $container)
$definitions = $container->getDefinitions();
foreach ($definitions as $id => $definition) {
- if ((!$definition->isPublic() || $definition->isPrivate()) && !$definition->getErrors() && !$definition->isAbstract()) {
+ if ($id && '.' !== $id[0] && (!$definition->isPublic() || $definition->isPrivate()) && !$definition->getErrors() && !$definition->isAbstract()) {
$privateServices[$id] = new ServiceClosureArgument(new Reference($id, ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE));
}
}
@@ -39,7 +39,7 @@ public function process(ContainerBuilder $container)
$aliases = $container->getAliases();
foreach ($aliases as $id => $alias) {
- if (!$alias->isPublic() || $alias->isPrivate()) {
+ if ($id && '.' !== $id[0] && (!$alias->isPublic() || $alias->isPrivate())) {
while (isset($aliases[$target = (string) $alias])) {
$alias = $aliases[$target];
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml
index d7aab2e068a58..f159208a41a28 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml
@@ -16,7 +16,7 @@
%test.client.parameters%
-
+ test.service_container
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
index 8dfc292073ed8..351d27900e83a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
@@ -123,6 +123,7 @@ protected static function ensureKernelShutdown()
$container->reset();
}
}
+ static::$container = null;
}
/**
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php
index 0a7d2391d55e9..d73118a22a5c1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AutowiringTypesTest.php
@@ -24,27 +24,24 @@ class AutowiringTypesTest extends WebTestCase
public function testAnnotationReaderAutowiring()
{
static::bootKernel(array('root_config' => 'no_annotations_cache.yml', 'environment' => 'no_annotations_cache'));
- $container = static::$kernel->getContainer();
- $annotationReader = $container->get('test.autowiring_types.autowired_services')->getAnnotationReader();
+ $annotationReader = static::$container->get('test.autowiring_types.autowired_services')->getAnnotationReader();
$this->assertInstanceOf(AnnotationReader::class, $annotationReader);
}
public function testCachedAnnotationReaderAutowiring()
{
static::bootKernel();
- $container = static::$kernel->getContainer();
- $annotationReader = $container->get('test.autowiring_types.autowired_services')->getAnnotationReader();
+ $annotationReader = static::$container->get('test.autowiring_types.autowired_services')->getAnnotationReader();
$this->assertInstanceOf(CachedReader::class, $annotationReader);
}
public function testTemplatingAutowiring()
{
static::bootKernel();
- $container = static::$kernel->getContainer();
- $autowiredServices = $container->get('test.autowiring_types.autowired_services');
+ $autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
$this->assertInstanceOf(FrameworkBundleEngineInterface::class, $autowiredServices->getFrameworkBundleEngine());
$this->assertInstanceOf(ComponentEngineInterface::class, $autowiredServices->getEngine());
}
@@ -52,24 +49,21 @@ public function testTemplatingAutowiring()
public function testEventDispatcherAutowiring()
{
static::bootKernel(array('debug' => false));
- $container = static::$kernel->getContainer();
- $autowiredServices = $container->get('test.autowiring_types.autowired_services');
+ $autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
$this->assertInstanceOf(EventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled');
static::bootKernel(array('debug' => true));
- $container = static::$kernel->getContainer();
- $autowiredServices = $container->get('test.autowiring_types.autowired_services');
+ $autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
$this->assertInstanceOf(TraceableEventDispatcher::class, $autowiredServices->getDispatcher(), 'The debug.event_dispatcher service should be injected if the debug is enabled');
}
public function testCacheAutowiring()
{
static::bootKernel();
- $container = static::$kernel->getContainer();
- $autowiredServices = $container->get('test.autowiring_types.autowired_services');
+ $autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
$this->assertInstanceOf(FilesystemAdapter::class, $autowiredServices->getCachePool());
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php
index 386a43424eae0..3a77541521200 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolClearCommandTest.php
@@ -77,9 +77,8 @@ public function testClearUnexistingPool()
private function createCommandTester()
{
- $container = static::$kernel->getContainer();
$application = new Application(static::$kernel);
- $application->add(new CachePoolClearCommand($container->get('cache.global_clearer')));
+ $application->add(new CachePoolClearCommand(static::$container->get('cache.global_clearer')));
return new CommandTester($application->find('cache:pool:clear'));
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php
index eafc798a4838b..9cdb93a493f20 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php
@@ -70,7 +70,7 @@ public function testRedisCustomCachePools()
private function doTestCachePools($options, $adapterClass)
{
static::bootKernel($options);
- $container = static::$kernel->getContainer();
+ $container = static::$container;
$pool1 = $container->get('cache.pool1');
$this->assertInstanceOf($adapterClass, $pool1);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
index 4bdf7592b45bc..21d9b1ca3b278 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php
@@ -26,12 +26,12 @@ public function testDumpContainerIfNotExists()
$application = new Application(static::$kernel);
$application->setAutoExit(false);
- @unlink(static::$kernel->getContainer()->getParameter('debug.container.dump'));
+ @unlink(static::$container->getParameter('debug.container.dump'));
$tester = new ApplicationTester($application);
$tester->run(array('command' => 'debug:container'));
- $this->assertFileExists(static::$kernel->getContainer()->getParameter('debug.container.dump'));
+ $this->assertFileExists(static::$container->getParameter('debug.container.dump'));
}
public function testNoDebug()
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php
index bc7dc12ebfbca..bdcf462baea22 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php
@@ -19,9 +19,8 @@ class SerializerTest extends WebTestCase
public function testDeserializeArrayOfObject()
{
static::bootKernel(array('test_case' => 'Serializer'));
- $container = static::$kernel->getContainer();
- $result = $container->get('serializer')->deserialize('{"bars": [{"id": 1}, {"id": 2}]}', Foo::class, 'json');
+ $result = static::$container->get('serializer')->deserialize('{"bars": [{"id": 1}, {"id": 2}]}', Foo::class, 'json');
$bar1 = new Bar();
$bar1->id = 1;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/TestServiceContainerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/TestServiceContainerTest.php
index 88d3b4cf29a31..b50c3e1f0b715 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/TestServiceContainerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/TestServiceContainerTest.php
@@ -42,6 +42,6 @@ public function testThatPrivateServicesAreAvailableIfTestConfigIsEnabled()
$this->assertTrue(static::$container->has(NonPublicService::class));
$this->assertTrue(static::$container->has(PrivateService::class));
$this->assertTrue(static::$container->has('private_service'));
- $this->assertTrue(static::$container->has(UnusedPrivateService::class));
+ $this->assertFalse(static::$container->has(UnusedPrivateService::class));
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php
index 3dd18944de9f3..ba523382b66ba 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php
@@ -26,7 +26,7 @@ class AddSessionDomainConstraintPass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container)
{
- if (!$container->hasParameter('session.storage.options') || !$container->has('security.http_utils')) {
+ if (!$container->hasParameter('session.storage.options')) {
return;
}
@@ -34,6 +34,7 @@ public function process(ContainerBuilder $container)
$domainRegexp = empty($sessionOptions['cookie_domain']) ? '%s' : sprintf('(?:%%s|(?:.+\.)?%s)', preg_quote(trim($sessionOptions['cookie_domain'], '.')));
$domainRegexp = (empty($sessionOptions['cookie_secure']) ? 'https?://' : 'https://').$domainRegexp;
+ // if the service doesn't exist, an exception must be thrown - ignoring would put security at risk
$container->findDefinition('security.http_utils')->addArgument(sprintf('{^%s$}i', $domainRegexp));
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php
new file mode 100644
index 0000000000000..d4d28ecc4eb35
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * @author Christian Flothmann
+ */
+class RegisterCsrfTokenClearingLogoutHandlerPass implements CompilerPassInterface
+{
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->has('security.logout_listener') || !$container->has('security.csrf.token_storage')) {
+ return;
+ }
+
+ $csrfTokenStorage = $container->findDefinition('security.csrf.token_storage');
+ $csrfTokenStorageClass = $container->getParameterBag()->resolveValue($csrfTokenStorage->getClass());
+
+ if (!is_subclass_of($csrfTokenStorageClass, 'Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface')) {
+ return;
+ }
+
+ $container->register('security.logout.handler.csrf_token_clearing', 'Symfony\Component\Security\Http\Logout\CsrfTokenClearingLogoutHandler')
+ ->addArgument(new Reference('security.csrf.token_storage'))
+ ->setPublic(false);
+
+ $container->findDefinition('security.logout_listener')->addMethodCall('addHandler', array(new Reference('security.logout.handler.csrf_token_clearing')));
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php
index da5d4c3559b68..dd6dd829d7494 100644
--- a/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php
+++ b/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php
@@ -11,6 +11,7 @@
namespace Symfony\Bundle\SecurityBundle;
+use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterCsrfTokenClearingLogoutHandlerPass;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\JsonLoginFactory;
use Symfony\Component\Console\Application;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -58,7 +59,8 @@ public function build(ContainerBuilder $container)
$extension->addUserProviderFactory(new InMemoryFactory());
$extension->addUserProviderFactory(new LdapFactory());
$container->addCompilerPass(new AddSecurityVotersPass());
- $container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_AFTER_REMOVING);
+ $container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_BEFORE_REMOVING);
+ $container->addCompilerPass(new RegisterCsrfTokenClearingLogoutHandlerPass());
}
public function registerCommands(Application $application)
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php
index 82205e5912969..8871a3346bbde 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSecurityVotersPassTest.php
@@ -88,3 +88,10 @@ public function testVoterMissingInterface()
$compilerPass->process($container);
}
}
+
+class VoterWithoutInterface
+{
+ public function vote()
+ {
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php
index 382bdebe018fa..a836ab136cd93 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php
@@ -96,6 +96,19 @@ public function testNoSession()
$this->assertTrue($utils->createRedirectResponse($request, 'http://pirate.com/foo')->isRedirect('http://pirate.com/foo'));
}
+ /**
+ * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
+ * @expectedExceptionMessage You have requested a non-existent service "security.http_utils".
+ */
+ public function testNoHttpUtils()
+ {
+ $container = new ContainerBuilder();
+ $container->setParameter('session.storage.options', array());
+
+ $pass = new AddSessionDomainConstraintPass();
+ $pass->process($container);
+ }
+
private function createContainer($sessionStorageOptions)
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php
index a0bee3c01c1ca..25a70577b2c1e 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AutowiringTypesTest.php
@@ -19,15 +19,13 @@ class AutowiringTypesTest extends WebTestCase
public function testAccessDecisionManagerAutowiring()
{
static::bootKernel(array('debug' => false));
- $container = static::$kernel->getContainer();
- $autowiredServices = $container->get('test.autowiring_types.autowired_services');
+ $autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
$this->assertInstanceOf(AccessDecisionManager::class, $autowiredServices->getAccessDecisionManager(), 'The security.access.decision_manager service should be injected in debug mode');
static::bootKernel(array('debug' => true));
- $container = static::$kernel->getContainer();
- $autowiredServices = $container->get('test.autowiring_types.autowired_services');
+ $autowiredServices = static::$container->get('test.autowiring_types.autowired_services');
$this->assertInstanceOf(TraceableAccessDecisionManager::class, $autowiredServices->getAccessDecisionManager(), 'The debug.security.access.decision_manager service should be injected in non-debug mode');
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
index 7eeb7c21171ce..d3c3b77fd5d61 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php
@@ -31,4 +31,22 @@ public function testSessionLessRememberMeLogout()
$this->assertNull($cookieJar->get('REMEMBERME'));
}
+
+ public function testCsrfTokensAreClearedOnLogout()
+ {
+ $client = $this->createClient(array('test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml'));
+ $client->getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar');
+
+ $client->request('POST', '/login', array(
+ '_username' => 'johannes',
+ '_password' => 'test',
+ ));
+
+ $this->assertTrue($client->getContainer()->get('security.csrf.token_storage')->hasToken('foo'));
+ $this->assertSame('bar', $client->getContainer()->get('security.csrf.token_storage')->getToken('foo'));
+
+ $client->request('GET', '/logout');
+
+ $this->assertFalse($client->getContainer()->get('security.csrf.token_storage')->hasToken('foo'));
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php
new file mode 100644
index 0000000000000..d90f774abde2b
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
+
+return array(
+ new FrameworkBundle(),
+ new SecurityBundle(),
+);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml
new file mode 100644
index 0000000000000..9e5563fea5197
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml
@@ -0,0 +1,26 @@
+imports:
+ - { resource: ./../config/framework.yml }
+
+security:
+ encoders:
+ Symfony\Component\Security\Core\User\User: plaintext
+
+ providers:
+ in_memory:
+ memory:
+ users:
+ johannes: { password: test, roles: [ROLE_USER] }
+
+ firewalls:
+ default:
+ form_login:
+ check_path: login
+ remember_me: true
+ require_previous_session: false
+ remember_me:
+ always_remember_me: true
+ secret: secret
+ logout:
+ invalidate_session: false
+ anonymous: ~
+ stateless: true
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/routing.yml
new file mode 100644
index 0000000000000..1dddfca2f8154
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/routing.yml
@@ -0,0 +1,5 @@
+login:
+ path: /login
+
+logout:
+ path: /logout
diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json
index 8a64bcea04c60..31ecaeebeb4a3 100644
--- a/src/Symfony/Bundle/SecurityBundle/composer.json
+++ b/src/Symfony/Bundle/SecurityBundle/composer.json
@@ -44,10 +44,10 @@
"twig/twig": "~1.34|~2.4"
},
"conflict": {
- "symfony/security": "4.1.0-beta1",
+ "symfony/security": "4.1.0-beta1|4.1.0-beta2",
"symfony/var-dumper": "<3.4",
"symfony/event-dispatcher": "<3.4",
- "symfony/framework-bundle": "<4.1",
+ "symfony/framework-bundle": "<=4.1-beta2",
"symfony/console": "<3.4"
},
"autoload": {
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig
index f9bc41d6a1b54..4752f6ef28fbf 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig
@@ -901,6 +901,7 @@ table.logs .metadata {
background: #FFF;
padding: 10px;
margin: 0.5em 0;
+ overflow: auto;
}
#collector-content .sf-validator .trace {
font-size: 12px;
@@ -932,6 +933,7 @@ table.logs .metadata {
background: #FFF;
padding: 10px;
margin: 0.5em 0;
+ overflow: auto;
}
#collector-content pre.sf-dump,
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
index 1bae24c10b458..b13f2bdc5f8b9 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\DependencyInjection\Compiler;
-use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
@@ -31,9 +30,6 @@ protected function processValue($value, $isRoot = false)
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $value->getInvalidBehavior() && !$this->container->has($id = (string) $value)) {
throw new ServiceNotFoundException($id, $this->currentId);
}
- if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior() && $this->container->has($id = (string) $value) && !$this->container->findDefinition($id)->isShared()) {
- throw new InvalidArgumentException(sprintf('Invalid ignore-on-uninitialized reference found in service "%s": target service "%s" is not shared.', $this->currentId, $id));
- }
return $value;
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
index b153b84764bc1..9aee66c8e0d5b 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php
@@ -88,11 +88,15 @@ protected function processValue($value, $isRoot = false)
*/
private function isInlineableDefinition($id, Definition $definition, ServiceReferenceGraph $graph)
{
+ if ($definition->getErrors() || $definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic()) {
+ return false;
+ }
+
if (!$definition->isShared()) {
return true;
}
- if ($definition->isDeprecated() || $definition->isPublic() || $definition->isLazy() || $definition->getErrors()) {
+ if ($definition->isPublic()) {
return false;
}
@@ -120,6 +124,6 @@ private function isInlineableDefinition($id, Definition $definition, ServiceRefe
return false;
}
- return $this->container->getDefinition($ids[0])->isShared();
+ return !$ids || $this->container->getDefinition($ids[0])->isShared();
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 8491f806b4b17..cfe9fdfcfb99f 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -1660,6 +1660,9 @@ private function getServiceCall(string $id, Reference $reference = null): string
if ($this->container->hasDefinition($id) && ($definition = $this->container->getDefinition($id)) && !$definition->isSynthetic()) {
if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
$code = 'null';
+ if (!$definition->isShared()) {
+ return $code;
+ }
} elseif ($this->isTrivialInstance($definition)) {
$code = substr($this->addNewInstance($definition, '', '', $id), 8, -2);
if ($definition->isShared()) {
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php
index 6e3d75bb0b9de..d258980d6660a 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php
@@ -54,17 +54,4 @@ public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionPa
return $prefix.$parent->name;
}
}
-
- private static function export($value)
- {
- if (!is_array($value)) {
- return var_export($value, true);
- }
- $code = array();
- foreach ($value as $k => $v) {
- $code[] = sprintf('%s => %s', var_export($k, true), self::export($v));
- }
-
- return sprintf('array(%s)', implode(', ', $code));
- }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php
index ac002d834d1c5..a3fbfcf10132f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php
@@ -68,27 +68,6 @@ public function testProcessThrowsExceptionOnInvalidReferenceFromInlinedDefinitio
$this->process($container);
}
- /**
- * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
- * @expectedExceptionMessage Invalid ignore-on-uninitialized reference found in service
- */
- public function testProcessThrowsExceptionOnNonSharedUninitializedReference()
- {
- $container = new ContainerBuilder();
-
- $container
- ->register('a', 'stdClass')
- ->addArgument(new Reference('b', $container::IGNORE_ON_UNINITIALIZED_REFERENCE))
- ;
-
- $container
- ->register('b', 'stdClass')
- ->setShared(false)
- ;
-
- $this->process($container);
- }
-
public function testProcessDefinitionWithBindings()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 4e00b708dcca7..d08d163927611 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\DependencyInjection\Tests\Dumper;
-use DummyProxyDumper;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\Config\FileLocator;
@@ -530,6 +529,19 @@ public function testInlinedDefinitionReferencingServiceContainer()
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
}
+ public function testNonSharedLazyDefinitionReferences()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setShared(false)->setLazy(true);
+ $container->register('bar', 'stdClass')->addArgument(new Reference('foo', ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, false))->setPublic(true);
+ $container->compile();
+
+ $dumper = new PhpDumper($container);
+ $dumper->setProxyDumper(new \DummyProxyDumper());
+
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_non_shared_lazy.php', $dumper->dump());
+ }
+
public function testInitializePropertiesBeforeMethodCalls()
{
require_once self::$fixturesPath.'/includes/classes.php';
@@ -598,7 +610,7 @@ public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServic
$dumper = new PhpDumper($container);
- $dumper->setProxyDumper(new DummyProxyDumper());
+ $dumper->setProxyDumper(new \DummyProxyDumper());
$dumper->dump();
$this->addToAssertionCount(1);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php
index bc653a744c9c5..bced911043c55 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/classes.php
@@ -85,17 +85,17 @@ class DummyProxyDumper implements ProxyDumper
{
public function isProxyCandidate(Definition $definition)
{
- return false;
+ return $definition->isLazy();
}
public function getProxyFactoryCode(Definition $definition, $id, $factoryCall = null)
{
- return '';
+ return " // lazy factory\n\n";
}
public function getProxyCode(Definition $definition)
{
- return '';
+ return "// proxy code\n";
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php
new file mode 100644
index 0000000000000..84a1dd80b3dee
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy.php
@@ -0,0 +1,90 @@
+services = $this->privates = array();
+ $this->methodMap = array(
+ 'bar' => 'getBarService',
+ );
+
+ $this->aliases = array();
+ }
+
+ public function reset()
+ {
+ $this->privates = array();
+ parent::reset();
+ }
+
+ public function compile()
+ {
+ throw new LogicException('You cannot compile a dumped container that was already compiled.');
+ }
+
+ public function isCompiled()
+ {
+ return true;
+ }
+
+ public function getRemovedIds()
+ {
+ return array(
+ 'Psr\\Container\\ContainerInterface' => true,
+ 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
+ 'foo' => true,
+ );
+ }
+
+ protected function createProxy($class, \Closure $factory)
+ {
+ return $factory();
+ }
+
+ /**
+ * Gets the public 'bar' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getBarService()
+ {
+ return $this->services['bar'] = new \stdClass($this->getFooService());
+ }
+
+ /**
+ * Gets the private 'foo' service.
+ *
+ * @return \stdClass
+ */
+ protected function getFooService($lazyLoad = true)
+ {
+ // lazy factory
+
+ return new \stdClass();
+ }
+}
+
+// proxy code
diff --git a/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php b/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php
index 0724f697ba77a..d5bcb04a4df6d 100644
--- a/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php
+++ b/src/Symfony/Component/Form/Test/Traits/ValidatorExtensionTrait.php
@@ -31,7 +31,7 @@ protected function getValidatorExtension()
}
$this->validator = $this->getMockBuilder(ValidatorInterface::class)->getMock();
- $metadata = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+ $metadata = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->setMethods(array('addPropertyConstraint'))->getMock();
$this->validator->expects($this->any())->method('getMetadataFor')->will($this->returnValue($metadata));
$this->validator->expects($this->any())->method('validate')->will($this->returnValue(array()));
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
index 1b77e1ff6accb..a2b4e2ef9fb6b 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
@@ -22,6 +22,7 @@ public function test2Dot5ValidationApi()
->disableOriginalConstructor()
->getMock();
$metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata')
+ ->setMethods(array('addConstraint', 'addPropertyConstraint'))
->disableOriginalConstructor()
->getMock();
diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
index e3ef45ef672cf..d78c76068234f 100644
--- a/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
+++ b/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
@@ -80,13 +80,8 @@ public static function reset()
*/
private function __construct()
{
- if (FileBinaryMimeTypeGuesser::isSupported()) {
- $this->register(new FileBinaryMimeTypeGuesser());
- }
-
- if (FileinfoMimeTypeGuesser::isSupported()) {
- $this->register(new FileinfoMimeTypeGuesser());
- }
+ $this->register(new FileBinaryMimeTypeGuesser());
+ $this->register(new FileinfoMimeTypeGuesser());
}
/**
@@ -125,18 +120,14 @@ public function guess($path)
throw new AccessDeniedException($path);
}
- if (!$this->guessers) {
- $msg = 'Unable to guess the mime type as no guessers are available';
- if (!FileinfoMimeTypeGuesser::isSupported()) {
- $msg .= ' (Did you enable the php_fileinfo extension?)';
- }
- throw new \LogicException($msg);
- }
-
foreach ($this->guessers as $guesser) {
if (null !== $mimeType = $guesser->guess($path)) {
return $mimeType;
}
}
+
+ if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) {
+ throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
+ }
}
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
index 43f07f25de1dd..0fd34dc1b65ff 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -616,6 +616,7 @@ protected function doRead($sessionId)
$selectSql = $this->getSelectSql();
$selectStmt = $this->pdo->prepare($selectSql);
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $insertStmt = null;
do {
$selectStmt->execute();
@@ -631,6 +632,11 @@ protected function doRead($sessionId)
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
}
+ if (null !== $insertStmt) {
+ $this->rollback();
+ throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
+ }
+
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
// In strict mode, session fixation is not possible: new sessions always start with a unique
// random id, so that concurrency is not possible and this code path can be skipped.
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc
index ba101d357852d..f9c40a9a3c5e1 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc
+++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/common.inc
@@ -22,6 +22,10 @@ error_reporting(-1);
ini_set('html_errors', 0);
ini_set('display_errors', 1);
+if (ini_get('xdebug.default_enable')) {
+ xdebug_disable();
+}
+
header_remove('X-Powered-By');
header('Content-Type: text/plain; charset=utf-8');
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
index 82061fd6ea0fc..f07647f8a67dc 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php
@@ -61,10 +61,12 @@ public function onKernelResponse(FilterResponseEvent $event)
return;
}
- if (!$session = $event->getRequest()->getSession()) {
+ $request = $event->getRequest();
+ if (!$request->hasSession()) {
return;
}
+ $session = $request->getSession();
if ($wasStarted = $session->isStarted()) {
$session->save();
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index ae3ea7243749f..4fbabf8d9bfcc 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -63,12 +63,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private $requestStackSize = 0;
private $resetServices = false;
- const VERSION = '4.1.0-BETA2';
+ const VERSION = '4.1.0-BETA3';
const VERSION_ID = 40100;
const MAJOR_VERSION = 4;
const MINOR_VERSION = 1;
const RELEASE_VERSION = 0;
- const EXTRA_VERSION = 'BETA2';
+ const EXTRA_VERSION = 'BETA3';
const END_OF_MAINTENANCE = '01/2019';
const END_OF_LIFE = '07/2019';
@@ -79,18 +79,10 @@ public function __construct(string $environment, bool $debug)
$this->debug = $debug;
$this->rootDir = $this->getRootDir();
$this->name = $this->getName();
-
- if ($this->debug) {
- $this->startTime = microtime(true);
- }
}
public function __clone()
{
- if ($this->debug) {
- $this->startTime = microtime(true);
- }
-
$this->booted = false;
$this->container = null;
$this->requestStackSize = 0;
@@ -108,10 +100,16 @@ public function boot()
$this->container->get('services_resetter')->reset();
}
$this->resetServices = false;
+ if ($this->debug) {
+ $this->startTime = microtime(true);
+ }
}
return;
}
+ if ($this->debug) {
+ $this->startTime = microtime(true);
+ }
if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) {
putenv('SHELL_VERBOSITY=3');
$_ENV['SHELL_VERBOSITY'] = 3;
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
index 22a2b71239874..84ca04a680d61 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php
@@ -123,6 +123,16 @@ public function testDoesNotImplementServiceSubscriberInterface()
$this->assertFalse(is_subclass_of(TestSessionListener::class, ServiceSubscriberInterface::class, 'Implementing ServiceSubscriberInterface would create a dep on the DI component, which eg Silex cannot afford'));
}
+ public function testDoesNotThrowIfRequestDoesNotHaveASession()
+ {
+ $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
+ $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, new Response());
+
+ $this->listener->onKernelResponse($event);
+
+ $this->assertTrue(true);
+ }
+
private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST)
{
$request->setSession($this->session);
diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php
index 9dab664cf5cdf..5a302d3090cf9 100644
--- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php
@@ -589,6 +589,21 @@ public function testServicesResetter()
$this->assertEquals(1, ResettableService::$counter);
}
+ /**
+ * @group time-sensitive
+ */
+ public function testKernelStartTimeIsResetWhileBootingAlreadyBootedKernel()
+ {
+ $kernel = $this->getKernelForTest(array('initializeBundles'), true);
+ $kernel->boot();
+ $preReBoot = $kernel->getStartTime();
+
+ sleep(3600); //Intentionally large value to detect if ClockMock ever breaks
+ $kernel->reboot(null);
+
+ $this->assertGreaterThan($preReBoot, $kernel->getStartTime());
+ }
+
/**
* Returns a mock for the BundleInterface.
*
@@ -658,10 +673,10 @@ protected function getKernel(array $methods = array(), array $bundles = array())
return $kernel;
}
- protected function getKernelForTest(array $methods = array())
+ protected function getKernelForTest(array $methods = array(), $debug = false)
{
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest')
- ->setConstructorArgs(array('test', false))
+ ->setConstructorArgs(array('test', $debug))
->setMethods($methods)
->getMock();
$p = new \ReflectionProperty($kernel, 'rootDir');
diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php b/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php
index 05315defccdfc..cd9e6b4c3e7f3 100644
--- a/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php
+++ b/src/Symfony/Component/Routing/Matcher/Dumper/StaticPrefixCollection.php
@@ -151,6 +151,7 @@ private function getCommonPrefix(string $prefix, string $anotherPrefix): array
$baseLength = \strlen($this->prefix);
$end = min(\strlen($prefix), \strlen($anotherPrefix));
$staticLength = null;
+ set_error_handler(array(__CLASS__, 'handleError'));
for ($i = $baseLength; $i < $end && $prefix[$i] === $anotherPrefix[$i]; ++$i) {
if ('(' === $prefix[$i]) {
@@ -174,13 +175,24 @@ private function getCommonPrefix(string $prefix, string $anotherPrefix): array
if (('?' === ($prefix[$j] ?? '') || '?' === ($anotherPrefix[$j] ?? '')) && ($prefix[$j] ?? '') !== ($anotherPrefix[$j] ?? '')) {
break;
}
+ $subPattern = substr($prefix, $i, $j - $i);
+ if ($prefix !== $anotherPrefix && !preg_match('/^\(\[[^\]]++\]\+\+\)$/', $subPattern) && !preg_match('{(? array(array('_route' => 'a', '_locale' => 'en'), array('_locale'), null, null),
43 => array(array('_route' => 'b', '_locale' => 'en'), array('_locale'), null, null),
- 58 => array(array('_route' => 'c', '_locale' => 'en'), array('_locale', 'id'), null, null),
- 73 => array(array('_route' => 'd', '_locale' => 'en'), array('_locale', 'id'), null, null),
- 86 => array(array('_route' => 'e', '_locale' => 'en'), array('_locale', 'id'), null, null),
- 104 => array(array('_route' => 'f', '_locale' => 'en'), array('_locale'), null, null),
- 120 => array(array('_route' => 'g', '_locale' => 'en'), array('_locale'), null, null),
- 144 => array(array('_route' => 'h', '_locale' => 'en'), array('_locale', 'page'), null, null),
- 165 => array(array('_route' => 'i', '_locale' => 'en'), array('_locale', 'page'), null, null),
- 192 => array(array('_route' => 'j', '_locale' => 'en'), array('_locale', 'id'), null, null),
- 206 => array(array('_route' => 'k', '_locale' => 'en'), array('_locale'), null, null),
- 223 => array(array('_route' => 'l', '_locale' => 'en'), array('_locale'), null, null),
- 234 => array(array('_route' => 'm', '_locale' => 'en'), array('_locale'), null, null),
- 253 => array(array('_route' => 'n', '_locale' => 'en'), array('_locale'), null, null),
+ 55 => array(array('_route' => 'c', '_locale' => 'en'), array('_locale', 'id'), null, null),
+ 72 => array(array('_route' => 'd', '_locale' => 'en'), array('_locale', 'id'), null, null),
+ 91 => array(array('_route' => 'e', '_locale' => 'en'), array('_locale', 'id'), null, null),
+ 107 => array(array('_route' => 'f', '_locale' => 'en'), array('_locale'), null, null),
+ 123 => array(array('_route' => 'g', '_locale' => 'en'), array('_locale'), null, null),
+ 147 => array(array('_route' => 'h', '_locale' => 'en'), array('_locale', 'page'), null, null),
+ 168 => array(array('_route' => 'i', '_locale' => 'en'), array('_locale', 'page'), null, null),
+ 195 => array(array('_route' => 'j', '_locale' => 'en'), array('_locale', 'id'), null, null),
+ 209 => array(array('_route' => 'k', '_locale' => 'en'), array('_locale'), null, null),
+ 226 => array(array('_route' => 'l', '_locale' => 'en'), array('_locale'), null, null),
+ 237 => array(array('_route' => 'm', '_locale' => 'en'), array('_locale'), null, null),
+ 256 => array(array('_route' => 'n', '_locale' => 'en'), array('_locale'), null, null),
);
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
@@ -139,7 +135,7 @@ private function doMatch(string $rawPathinfo, array &$allow = array(), array &$a
return $ret;
}
- if (253 === $m) {
+ if (256 === $m) {
break;
}
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
index 0ba61c948610f..b3bfd78271bf3 100644
--- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
+++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
@@ -611,6 +611,16 @@ public function testRequirementWithCapturingGroup()
$this->assertEquals(array('_route' => 'a', 'a' => 'a', 'b' => 'b'), $matcher->match('/a/b'));
}
+ public function testDotAllWithCatchAll()
+ {
+ $coll = new RouteCollection();
+ $coll->add('a', new Route('/{id}.html', array(), array('id' => '.+')));
+ $coll->add('b', new Route('/{all}', array(), array('all' => '.+')));
+
+ $matcher = $this->getUrlMatcher($coll);
+ $this->assertEquals(array('_route' => 'a', 'id' => 'foo/bar'), $matcher->match('/foo/bar.html'));
+ }
+
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
{
return new UrlMatcher($routes, $context ?: new RequestContext());
diff --git a/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php b/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php
index cdb4f8767a3ad..1b033cfacc685 100644
--- a/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Encoder/Argon2iPasswordEncoderTest.php
@@ -30,7 +30,7 @@ protected function setUp()
public function testValidationWithConfig()
{
- $encoder = new Argon2iPasswordEncoder(4, 4, 1);
+ $encoder = new Argon2iPasswordEncoder(8, 4, 1);
$result = $encoder->encodePassword(self::PASSWORD, null);
$this->assertTrue($encoder->isPasswordValid($result, self::PASSWORD, null));
$this->assertFalse($encoder->isPasswordValid($result, 'anotherPassword', null));
diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php
index 0a359d079da19..0ce8eca92aa4e 100644
--- a/src/Symfony/Component/Security/Core/User/UserInterface.php
+++ b/src/Symfony/Component/Security/Core/User/UserInterface.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\Security\Core\User;
+use Symfony\Component\Security\Core\Role\Role;
+
/**
* Represents the interface that all user classes must implement.
*
diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php
index 66197c95ac2d2..c8d31093ceff2 100644
--- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php
+++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php
@@ -113,4 +113,32 @@ public function testRemoveExistingToken()
$this->assertSame('TOKEN', $this->storage->removeToken('token_id'));
$this->assertFalse($this->storage->hasToken('token_id'));
}
+
+ public function testClearRemovesAllTokensFromTheConfiguredNamespace()
+ {
+ $this->storage->setToken('foo', 'bar');
+ $this->storage->clear();
+
+ $this->assertFalse($this->storage->hasToken('foo'));
+ $this->assertArrayNotHasKey(self::SESSION_NAMESPACE, $_SESSION);
+ }
+
+ public function testClearDoesNotRemoveSessionValuesFromOtherNamespaces()
+ {
+ $_SESSION['foo']['bar'] = 'baz';
+ $this->storage->clear();
+
+ $this->assertArrayHasKey('foo', $_SESSION);
+ $this->assertArrayHasKey('bar', $_SESSION['foo']);
+ $this->assertSame('baz', $_SESSION['foo']['bar']);
+ }
+
+ public function testClearDoesNotRemoveNonNamespacedSessionValues()
+ {
+ $_SESSION['foo'] = 'baz';
+ $this->storage->clear();
+
+ $this->assertArrayHasKey('foo', $_SESSION);
+ $this->assertSame('baz', $_SESSION['foo']);
+ }
}
diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
index 306e19ad91bb9..7539852f13f3f 100644
--- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
+++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php
@@ -129,4 +129,31 @@ public function testRemoveExistingTokenFromActiveSession()
$this->assertSame('TOKEN', $this->storage->removeToken('token_id'));
}
+
+ public function testClearRemovesAllTokensFromTheConfiguredNamespace()
+ {
+ $this->storage->setToken('foo', 'bar');
+ $this->storage->clear();
+
+ $this->assertFalse($this->storage->hasToken('foo'));
+ $this->assertFalse($this->session->has(self::SESSION_NAMESPACE.'/foo'));
+ }
+
+ public function testClearDoesNotRemoveSessionValuesFromOtherNamespaces()
+ {
+ $this->session->set('foo/bar', 'baz');
+ $this->storage->clear();
+
+ $this->assertTrue($this->session->has('foo/bar'));
+ $this->assertSame('baz', $this->session->get('foo/bar'));
+ }
+
+ public function testClearDoesNotRemoveNonNamespacedSessionValues()
+ {
+ $this->session->set('foo', 'baz');
+ $this->storage->clear();
+
+ $this->assertTrue($this->session->has('foo'));
+ $this->assertSame('baz', $this->session->get('foo'));
+ }
}
diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/ClearableTokenStorageInterface.php b/src/Symfony/Component/Security/Csrf/TokenStorage/ClearableTokenStorageInterface.php
new file mode 100644
index 0000000000000..0d6f16b68d0b6
--- /dev/null
+++ b/src/Symfony/Component/Security/Csrf/TokenStorage/ClearableTokenStorageInterface.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Csrf\TokenStorage;
+
+/**
+ * @author Christian Flothmann
+ */
+interface ClearableTokenStorageInterface extends TokenStorageInterface
+{
+ /**
+ * Removes all CSRF tokens.
+ */
+ public function clear();
+}
diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php
index d79e98d212c5d..aa59240be0820 100644
--- a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php
+++ b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php
@@ -18,7 +18,7 @@
*
* @author Bernhard Schussek
*/
-class NativeSessionTokenStorage implements TokenStorageInterface
+class NativeSessionTokenStorage implements ClearableTokenStorageInterface
{
/**
* The namespace used to store values in the session.
@@ -102,6 +102,14 @@ public function removeToken($tokenId)
return $token;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ unset($_SESSION[$this->namespace]);
+ }
+
private function startSession()
{
if (PHP_SESSION_NONE === session_status()) {
diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php
index a13f367b0ec30..67f93ec6724b0 100644
--- a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php
+++ b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php
@@ -19,7 +19,7 @@
*
* @author Bernhard Schussek
*/
-class SessionTokenStorage implements TokenStorageInterface
+class SessionTokenStorage implements ClearableTokenStorageInterface
{
/**
* The namespace used to store values in the session.
@@ -92,4 +92,16 @@ public function removeToken($tokenId)
return $this->session->remove($this->namespace.'/'.$tokenId);
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ foreach (array_keys($this->session->all()) as $key) {
+ if (0 === strpos($key, $this->namespace.'/')) {
+ $this->session->remove($key);
+ }
+ }
+ }
}
diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php
index 0af68b16a56a9..3cdf1d12a45b0 100644
--- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php
+++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php
@@ -48,6 +48,7 @@ public function __construct(TokenStorageInterface $tokenStorage, EventDispatcher
*/
public function authenticateWithToken(TokenInterface $token, Request $request)
{
+ $this->migrateSession($request);
$this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) {
@@ -108,4 +109,12 @@ public function handleAuthenticationFailure(AuthenticationException $authenticat
is_object($response) ? get_class($response) : gettype($response)
));
}
+
+ private function migrateSession(Request $request)
+ {
+ if (!$request->hasSession() || !$request->hasPreviousSession()) {
+ return;
+ }
+ $request->getSession()->migrate(true);
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
index 1057868ddb337..b933b4e5f8668 100644
--- a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
@@ -82,6 +82,9 @@ final public function handle(GetResponseEvent $event)
if (null !== $this->logger) {
$this->logger->info('Pre-authentication successful.', array('token' => (string) $token));
}
+
+ $this->migrateSession($request);
+
$this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) {
@@ -114,4 +117,12 @@ private function clearToken(AuthenticationException $exception)
* @return array An array composed of the user and the credentials
*/
abstract protected function getPreAuthenticatedData(Request $request);
+
+ private function migrateSession(Request $request)
+ {
+ if (!$request->hasSession() || !$request->hasPreviousSession()) {
+ return;
+ }
+ $request->getSession()->migrate(true);
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
index f8a01ad405ab0..ca44fcdb4c730 100644
--- a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
@@ -70,6 +71,9 @@ public function handle(GetResponseEvent $event)
try {
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
+
+ $this->migrateSession($request);
+
$this->tokenStorage->setToken($token);
} catch (AuthenticationException $e) {
$token = $this->tokenStorage->getToken();
@@ -88,4 +92,12 @@ public function handle(GetResponseEvent $event)
$event->setResponse($this->authenticationEntryPoint->start($request, $e));
}
}
+
+ private function migrateSession(Request $request)
+ {
+ if (!$request->hasSession() || !$request->hasPreviousSession()) {
+ return;
+ }
+ $request->getSession()->migrate(true);
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php
index ec607bb4574be..c1f492b4e7aca 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Security\Http\Firewall;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -77,6 +78,9 @@ public function handle(GetResponseEvent $event)
}
$token = $this->authenticationManager->authenticate($token);
+
+ $this->migrateSession($request);
+
$this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) {
@@ -111,4 +115,12 @@ public function handle(GetResponseEvent $event)
}
}
}
+
+ private function migrateSession(Request $request)
+ {
+ if (!$request->hasSession() || !$request->hasPreviousSession()) {
+ return;
+ }
+ $request->getSession()->migrate(true);
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php
index 5a977b855ff2e..3707c8669901b 100644
--- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php
@@ -139,6 +139,8 @@ private function onSuccess(Request $request, TokenInterface $token)
$this->logger->info('User has been authenticated successfully.', array('username' => $token->getUsername()));
}
+ $this->migrateSession($request);
+
$this->tokenStorage->setToken($token);
if (null !== $this->eventDispatcher) {
@@ -182,4 +184,12 @@ private function onFailure(Request $request, AuthenticationException $failed)
return $response;
}
+
+ private function migrateSession(Request $request)
+ {
+ if (!$request->hasSession() || !$request->hasPreviousSession()) {
+ return;
+ }
+ $request->getSession()->migrate(true);
+ }
}
diff --git a/src/Symfony/Component/Security/Http/Logout/CsrfTokenClearingLogoutHandler.php b/src/Symfony/Component/Security/Http/Logout/CsrfTokenClearingLogoutHandler.php
new file mode 100644
index 0000000000000..ad6b888aad562
--- /dev/null
+++ b/src/Symfony/Component/Security/Http/Logout/CsrfTokenClearingLogoutHandler.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Http\Logout;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface;
+
+/**
+ * @author Christian Flothmann
+ */
+class CsrfTokenClearingLogoutHandler implements LogoutHandlerInterface
+{
+ private $csrfTokenStorage;
+
+ public function __construct(ClearableTokenStorageInterface $csrfTokenStorage)
+ {
+ $this->csrfTokenStorage = $csrfTokenStorage;
+ }
+
+ public function logout(Request $request, Response $response, TokenInterface $token)
+ {
+ $this->csrfTokenStorage->clear();
+ }
+}
diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php
index ed055f20949fc..c61fe349f8f42 100644
--- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php
+++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php
@@ -47,6 +47,8 @@ public function onAuthentication(Request $request, TokenInterface $token)
return;
case self::MIGRATE:
+ // Note: this logic is duplicated in several authentication listeners
+ // until Symfony 5.0 due to a security fix with BC compat
$request->getSession()->migrate(true);
return;
diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php
index 9b05f151340ee..8de89b1868d16 100644
--- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php
+++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php
@@ -27,8 +27,8 @@ interface SessionAuthenticationStrategyInterface
/**
* This performs any necessary changes to the session.
*
- * This method is called before the TokenStorage is populated with a
- * Token, and only by classes inheriting from AbstractAuthenticationListener.
+ * This method should be called before the TokenStorage is populated with a
+ * Token. It should be used by authentication listeners when a session is used.
*/
public function onAuthentication(Request $request, TokenInterface $token);
}
diff --git a/src/Symfony/Component/Security/Http/Tests/Logout/CsrfTokenClearingLogoutHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/Logout/CsrfTokenClearingLogoutHandlerTest.php
new file mode 100644
index 0000000000000..fe34eaa6e5da3
--- /dev/null
+++ b/src/Symfony/Component/Security/Http/Tests/Logout/CsrfTokenClearingLogoutHandlerTest.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Http\Tests\Logout;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
+use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
+use Symfony\Component\Security\Http\Logout\CsrfTokenClearingLogoutHandler;
+
+class CsrfTokenClearingLogoutHandlerTest extends TestCase
+{
+ private $session;
+ private $csrfTokenStorage;
+ private $csrfTokenClearingLogoutHandler;
+
+ protected function setUp()
+ {
+ $this->session = new Session(new MockArraySessionStorage());
+ $this->csrfTokenStorage = new SessionTokenStorage($this->session, 'foo');
+ $this->csrfTokenStorage->setToken('foo', 'bar');
+ $this->csrfTokenStorage->setToken('foobar', 'baz');
+ $this->csrfTokenClearingLogoutHandler = new CsrfTokenClearingLogoutHandler($this->csrfTokenStorage);
+ }
+
+ public function testCsrfTokenCookieWithSameNamespaceIsRemoved()
+ {
+ $this->assertSame('bar', $this->session->get('foo/foo'));
+ $this->assertSame('baz', $this->session->get('foo/foobar'));
+
+ $this->csrfTokenClearingLogoutHandler->logout(new Request(), new Response(), $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock());
+
+ $this->assertFalse($this->csrfTokenStorage->hasToken('foo'));
+ $this->assertFalse($this->csrfTokenStorage->hasToken('foobar'));
+
+ $this->assertFalse($this->session->has('foo/foo'));
+ $this->assertFalse($this->session->has('foo/foobar'));
+ }
+
+ public function testCsrfTokenCookieWithDifferentNamespaceIsNotRemoved()
+ {
+ $barNamespaceCsrfSessionStorage = new SessionTokenStorage($this->session, 'bar');
+ $barNamespaceCsrfSessionStorage->setToken('foo', 'bar');
+ $barNamespaceCsrfSessionStorage->setToken('foobar', 'baz');
+
+ $this->assertSame('bar', $this->session->get('foo/foo'));
+ $this->assertSame('baz', $this->session->get('foo/foobar'));
+ $this->assertSame('bar', $this->session->get('bar/foo'));
+ $this->assertSame('baz', $this->session->get('bar/foobar'));
+
+ $this->csrfTokenClearingLogoutHandler->logout(new Request(), new Response(), $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock());
+
+ $this->assertTrue($barNamespaceCsrfSessionStorage->hasToken('foo'));
+ $this->assertTrue($barNamespaceCsrfSessionStorage->hasToken('foobar'));
+ $this->assertSame('bar', $barNamespaceCsrfSessionStorage->getToken('foo'));
+ $this->assertSame('baz', $barNamespaceCsrfSessionStorage->getToken('foobar'));
+ $this->assertFalse($this->csrfTokenStorage->hasToken('foo'));
+ $this->assertFalse($this->csrfTokenStorage->hasToken('foobar'));
+
+ $this->assertFalse($this->session->has('foo/foo'));
+ $this->assertFalse($this->session->has('foo/foobar'));
+ $this->assertSame('bar', $this->session->get('bar/foo'));
+ $this->assertSame('baz', $this->session->get('bar/foobar'));
+ }
+}
diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json
index 7bf03b6e874d4..95e9805673a66 100644
--- a/src/Symfony/Component/Security/Http/composer.json
+++ b/src/Symfony/Component/Security/Http/composer.json
@@ -25,9 +25,12 @@
},
"require-dev": {
"symfony/routing": "~3.4|~4.0",
- "symfony/security-csrf": "~3.4|~4.0",
+ "symfony/security-csrf": "^3.4.11|^4.0.11",
"psr/log": "~1.0"
},
+ "conflict": {
+ "symfony/security-csrf": "<3.4.11|~4.0,<4.0.11|~4.1,<=4.1.0-beta2"
+ },
"suggest": {
"symfony/security-csrf": "For using tokens to protect authentication/logout attempts",
"symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs"
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
index 9c97ff9893fa6..c6a41332a721c 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -376,6 +376,7 @@ private function isMaxDepthReached(array $attributesMetadata, string $class, str
{
if (
!isset($context[static::ENABLE_MAX_DEPTH]) ||
+ !$context[static::ENABLE_MAX_DEPTH] ||
!isset($attributesMetadata[$attribute]) ||
null === $maxDepth = $attributesMetadata[$attribute]->getMaxDepth()
) {
diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php
index 2ba258ecb7271..bc9aa60bd3a08 100644
--- a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php
@@ -32,21 +32,37 @@ public function normalize($object, $format = null, array $context = array())
$violations = array();
$messages = array();
foreach ($object as $violation) {
- $violations[] = array(
- 'propertyPath' => $violation->getPropertyPath(),
- 'message' => $violation->getMessage(),
- 'code' => $violation->getCode(),
- );
$propertyPath = $violation->getPropertyPath();
+
+ $violationEntry = array(
+ 'propertyPath' => $propertyPath,
+ 'title' => $violation->getMessage(),
+ );
+ if (null !== $code = $violation->getCode()) {
+ $violationEntry['type'] = sprintf('urn:uuid:%s', $code);
+ }
+
+ $violations[] = $violationEntry;
+
$prefix = $propertyPath ? sprintf('%s: ', $propertyPath) : '';
$messages[] = $prefix.$violation->getMessage();
}
- return array(
- 'title' => isset($context['title']) ? $context['title'] : 'An error occurred',
- 'detail' => $messages ? implode("\n", $messages) : '',
- 'violations' => $violations,
+ $result = array(
+ 'type' => $context['type'] ?? 'https://symfony.com/errors/validation',
+ 'title' => $context['title'] ?? 'Validation Failed',
);
+ if (isset($context['status'])) {
+ $result['status'] = $context['status'];
+ }
+ if ($messages) {
+ $result['detail'] = implode("\n", $messages);
+ }
+ if (isset($context['instance'])) {
+ $result['instance'] = $context['instance'];
+ }
+
+ return $result + array('violations' => $violations);
}
/**
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php
index 5c9c55028ff2f..9e8aec51477ba 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php
@@ -43,19 +43,20 @@ public function testNormalize()
));
$expected = array(
- 'title' => 'An error occurred',
+ 'type' => 'https://symfony.com/errors/validation',
+ 'title' => 'Validation Failed',
'detail' => 'd: a
4: 1',
'violations' => array(
array(
'propertyPath' => 'd',
- 'message' => 'a',
- 'code' => 'f',
+ 'title' => 'a',
+ 'type' => 'urn:uuid:f',
),
array(
'propertyPath' => '4',
- 'message' => '1',
- 'code' => '6',
+ 'title' => '1',
+ 'type' => 'urn:uuid:6',
),
),
);
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf
index 834db4015e8e4..6f5fd98ca192e 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf
@@ -314,6 +314,10 @@
This is not a valid Business Identifier Code (BIC).
To ni veljavna identifikacijska koda podjetja (BIC).
+
+ Error
+ Napaka
+