diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md
index d685302315bcb..5a003a6343fe2 100644
--- a/CHANGELOG-6.2.md
+++ b/CHANGELOG-6.2.md
@@ -7,6 +7,42 @@ in 6.2 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/v6.2.0...v6.2.1
+* 6.2.11 (2023-05-27)
+
+ * bug #50442 [SecurityBundle] Update security-1.0.xsd to include missing access-token definition (aegypius)
+ * bug #50429 [Console] block input stream if needed (joelwurtz)
+ * bug #50312 [Security] Skip clearing CSRF Token on stateless logout (chalasr)
+ * bug #50315 [Translation] Fix handling of null messages in `ArrayLoader` (rob006)
+ * bug #50338 [Console] Remove ``exec`` and replace it by ``shell_exec`` (maxbeckers)
+ * bug #50193 [Serializer] Fix `SerializedPath` not working with constructor arguments (HypeMC)
+ * bug #50280 [PropertyAccess] Fix nullsafe operator on array index (HypeMC)
+ * bug #50362 [FrameworkBundle] Fix Workflow without a marking store definition uses marking store definition of previously defined workflow (krciga22)
+ * bug #50309 [HttpFoundation] UrlHelper is now aware of RequestContext changes (giosh94mhz)
+ * bug #50309 [HttpFoundation] UrlHelper is now aware of RequestContext changes (giosh94mhz)
+ * bug #50352 [Notifier][TurboSMS] Fix get sender name (ZhukV)
+ * bug #50354 [Process] Stop the process correctly even if underlying input stream is not closed (joelwurtz)
+ * bug #50332 [PropertyInfo] Fix `PhpStanExtractor` when constructor has no docblock (HypeMC)
+ * bug #50253 [FrameworkBundle] Generate caches consistently on successive run of `cache:clear` command (Okhoshi)
+ * bug #49063 [Messenger] Respect `isRetryable` decision of the retry strategy for re-delivery (FlyingDR)
+ * bug #50251 [Serializer] Handle datetime deserialization in U format (tugmaks)
+ * bug #50266 [HttpFoundation] Fix file streaming after connection aborted (rlshukhov)
+ * bug #50277 [Messenger] Add `IS_REPEATABLE` flag to `AsMessageHandler` attribute (adrianguenter)
+ * bug #50269 Fix param type annotation (l-vo)
+ * bug #50268 Allow resources in Query::setParam (l-vo)
+ * bug #50256 [HttpClient] Fix setting duplicate-name headers when redirecting with AmpHttpClient (nicolas-grekas)
+ * bug #50214 [WebProfilerBundle] Remove legacy filters remnants (MatTheCat)
+ * bug #50235 [HttpClient] Fix getting through proxies via CONNECT (nicolas-grekas)
+ * bug #50241 [HttpKernel] Prevent initialising lazy services during services reset (tucksaun)
+ * bug #50244 [HttpKernel] Fix restoring surrogate content from cache (nicolas-grekas)
+ * bug #50246 [DependencyInjection] Do not check errored definitions’ type (MatTheCat)
+ * bug #49557 [PropertyInfo] Fix phpDocExtractor nullable array value type (fabpot)
+ * bug #50213 [ErrorHandler] Prevent conflicts with WebProfilerBundle’s JavaScript (MatTheCat)
+ * bug #50192 [Serializer] backed enum throw notNormalizableValueException outside construct method (alli83)
+ * bug #50238 [HttpKernel] Don't use eval() to render ESI/SSI (nicolas-grekas)
+ * bug #50226 [HttpClient] Ensure HttplugClient ignores invalid HTTP headers (nicolas-grekas)
+ * bug #50203 [Messenger] Fix registering message handlers (nicolas-grekas)
+ * bug #50204 [ErrorHandler] Skip Httplug deprecations for HttplugClient (nicolas-grekas)
+
* 6.2.10 (2023-04-28)
* bug #50143 [Console] trim(): Argument #1 () must be of type string, bool given (danepowell)
diff --git a/CHANGELOG-6.3.md b/CHANGELOG-6.3.md
index 73ef0487e49f5..e5d6c9162a7c5 100644
--- a/CHANGELOG-6.3.md
+++ b/CHANGELOG-6.3.md
@@ -7,6 +7,21 @@ in 6.3 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.3.0...v6.3.1
+* 6.3.0 (2023-05-30)
+
+ * bug #50432 [Security] Validate `aud` and `iss` claims on OidcTokenHandler (vincentchalamon)
+ * bug #50477 [Security] Add clock dependency to OidcTokenHandler (nicolas-grekas)
+ * bug #50437 [Filesystem] Follow symlinks when dumping files (ausi)
+ * bug #50478 [DependencyInjection] Escape `` from parameter-like default values (MatTheCat)
+ * bug #50476 [FrameworkBundle] remove support for preloading ESM using headers (dunglas)
+ * bug #50453 [SecurityBundle] add missing xsd definition for OIDC (aegypius)
+ * bug #50468 [FrameworkBundle][PhpUnitBridge] Configure doctrine/deprecations as expected (nicolas-grekas)
+ * bug #50473 [Notifier] Fix ContactEveryoneOptions (nicolas-grekas)
+ * bug #50470 [SecurityBundle] Fix configuring OIDC user info token handler client (vincentchalamon)
+ * bug #50456 [AssetMapper] Fix unable to use asset mapper with CSP (vtsykun)
+ * bug #50458 [HttpKernel] Fix default value ignored with pinned resolvers (HypeMC)
+ * bug #50452 Hide definitions bearing the `container.excluded` tag (Myks92)
+
* 6.3.0-RC2 (2023-05-27)
* feature #50445 [AssetMapper] Add "=alias" syntax to importmap:require (weaverryan)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 24afd64907140..cf5f960f7a101 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -21,9 +21,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Jordi Boggiano (seldaek)
- Roland Franssen (ro0)
- Victor Berchet (victor)
+ - Javier Eguiluz (javier.eguiluz)
- Yonel Ceruto (yonelceruto)
- Tobias Nyholm (tobias)
- - Javier Eguiluz (javier.eguiluz)
- Oskar Stark (oskarstark)
- Ryan Weaver (weaverryan)
- Johannes S (johannes)
@@ -40,8 +40,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Abdellatif Ait boudad (aitboudad)
- Jan Schädlich (jschaedl)
- Lukas Kahwe Smith (lsmith)
- - Jérôme Tamarelle (gromnan)
- Kevin Bond (kbond)
+ - Jérôme Tamarelle (gromnan)
- Martin Hasoň (hason)
- Jeremy Mikola (jmikola)
- Jean-François Simon (jfsimon)
@@ -52,21 +52,21 @@ The Symfony Connect username in parenthesis allows to get more information
- Valentin Udaltsov (vudaltsov)
- Vasilij Duško (staff)
- Matthias Pigulla (mpdude)
+ - Antoine Lamirault (alamirault)
- Gabriel Ostrolucký (gadelat)
- - Antoine Makdessi (amakdessi)
- Laurent VOULLEMIER (lvo)
+ - Antoine Makdessi (amakdessi)
- Pierre du Plessis (pierredup)
- - Antoine Lamirault (alamirault)
- Grégoire Paris (greg0ire)
- Jonathan Wage (jwage)
+ - Mathieu Lechat (mat_the_cat)
- Titouan Galopin (tgalopin)
- David Maicher (dmaicher)
+ - Alexander Schranz (alexander-schranz)
- Gábor Egyed (1ed)
- Mathieu Santostefano (welcomattic)
- - Alexander Schranz (alexander-schranz)
- Alexandre Salomé (alexandresalome)
- William DURAND
- - Mathieu Lechat (mat_the_cat)
- ornicar
- Dany Maillard (maidmaid)
- Eriksen Costa
@@ -76,10 +76,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Francis Besset (francisbesset)
- Vasilij Dusko | CREATION
- Bulat Shakirzyanov (avalanche123)
+ - Vincent Langlet (deviling)
- Iltar van der Berg
- Miha Vrhovnik (mvrhov)
- Mathieu Piot (mpiot)
- - Vincent Langlet (deviling)
- Saša Stamenković (umpirsky)
- Alex Pott
- Guilhem N (guilhemn)
@@ -95,14 +95,14 @@ The Symfony Connect username in parenthesis allows to get more information
- Ruud Kamphuis (ruudk)
- Henrik Bjørnskov (henrikbjorn)
- David Buchmann (dbu)
+ - Massimiliano Arione (garak)
- Andrej Hudec (pulzarraider)
- Julien Falque (julienfalque)
- - Massimiliano Arione (garak)
- Jáchym Toušek (enumag)
- Douglas Greenshields (shieldo)
+ - Mathias Arlaud (mtarld)
- Christian Raue
- Fran Moreno (franmomu)
- - Mathias Arlaud (mtarld)
- Graham Campbell (graham)
- Michel Weimerskirch (mweimerskirch)
- Eric Clemmons (ericclemmons)
@@ -117,18 +117,18 @@ The Symfony Connect username in parenthesis allows to get more information
- Dariusz Górecki (canni)
- Maxime Helias (maxhelias)
- Ener-Getick
- - Sebastiaan Stok (sstok)
- Tugdual Saunier (tucksaun)
+ - Sebastiaan Stok (sstok)
- Jérôme Vasseur (jvasseur)
- Ion Bazan (ionbazan)
- Rokas Mikalkėnas (rokasm)
+ - Yanick Witschi (toflar)
- Lee McDermott
- Brandon Turner
- Luis Cordova (cordoval)
- Daniel Holmes (dholmes)
- Toni Uebernickel (havvg)
- Bart van den Burg (burgov)
- - Yanick Witschi (toflar)
- Jordan Alliot (jalliot)
- Smaine Milianni (ismail1432)
- John Wards (johnwards)
@@ -137,6 +137,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Antoine Hérault (herzult)
- Konstantin.Myakshin
- Arman Hosseini (arman)
+ - gnito-org
- Saif Eddin Gmati (azjezz)
- Simon Berger
- Arnaud Le Blanc (arnaud-lb)
@@ -152,6 +153,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Chris Wilkinson (thewilkybarkid)
- Brice BERNARD (brikou)
- Roman Martinuk (a2a4)
+ - Joel Wurtz (brouznouf)
- Gregor Harlan (gharlan)
- Baptiste Clavié (talus)
- Adrien Brault (adrienbrault)
@@ -168,7 +170,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Guillaume (guill)
- Christopher Hertel (chertel)
- Jacob Dreesen (jdreesen)
- - Joel Wurtz (brouznouf)
- Olivier Dolbeau (odolbeau)
- Florian Voutzinos (florianv)
- zairig imad (zairigimad)
@@ -180,6 +181,7 @@ The Symfony Connect username in parenthesis allows to get more information
- HeahDude
- Richard van Laak (rvanlaak)
- Paráda József (paradajozsef)
+ - Hubert Lenoir (hubert_lenoir)
- Alessandro Lai (jean85)
- Alexander Schwenn (xelaris)
- Fabien Pennequin (fabienpennequin)
@@ -203,7 +205,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Tigran Azatyan (tigranazatyan)
- Eric GELOEN (gelo)
- Matthieu Napoli (mnapoli)
- - Hubert Lenoir (hubert_lenoir)
- Tomáš Votruba (tomas_votruba)
- Joshua Thijssen
- Stefano Sala (stefano.sala)
@@ -295,7 +296,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Samuel NELA (snela)
- Romain Monteil (ker0x)
- dFayet
- - gnito-org
- Karoly Gossler (connorhu)
- Vincent AUBERT (vincent)
- Sebastien Morel (plopix)
@@ -304,7 +304,9 @@ The Symfony Connect username in parenthesis allows to get more information
- Timothée Barray (tyx)
- Sébastien Alfaiate (seb33300)
- James Halsall (jaitsu)
+ - Maximilian Beckers (maxbeckers)
- Mikael Pajunen
+ - Marcin Sikoń (marphi)
- Warnar Boekkooi (boekkooi)
- Marco Petersen (ocrampete16)
- Benjamin Leveque (benji07)
@@ -335,6 +337,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Urinbayev Shakhobiddin (shokhaa)
- Ahmed Raafat
- Philippe Segatori
+ - Allison Guilhem (a_guilhem)
- Thibaut Cheymol (tcheymol)
- Julien Pauli
- Islam Israfilov (islam93)
@@ -346,7 +349,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Ruben Gonzalez (rubenrua)
- Benjamin Dulau (dbenjamin)
- Pavel Kirpitsov (pavel-kirpichyov)
- - Maximilian Beckers (maxbeckers)
- Mathieu Lemoine (lemoinem)
- Christian Schmidt
- Andreas Hucks (meandmymonkey)
@@ -357,7 +359,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Clara van Miert
- Martin Auswöger
- Alexander Menshchikov
- - Marcin Sikoń (marphi)
- Stepan Anchugov (kix)
- bronze1man
- sun (sun)
@@ -375,11 +376,13 @@ The Symfony Connect username in parenthesis allows to get more information
- Kyle
- Dominique Bongiraud
- Hidde Wieringa (hiddewie)
+ - Dane Powell
- Christopher Davis (chrisguitarguy)
- Lukáš Holeczy (holicz)
- Michael Lee (zerustech)
- Florian Lonqueu-Brochard (florianlb)
- Leszek Prabucki (l3l0)
+ - Giorgio Premi
- Emanuele Panzeri (thepanz)
- Matthew Smeets
- François Zaninotto (fzaninotto)
@@ -415,14 +418,12 @@ The Symfony Connect username in parenthesis allows to get more information
- Mantis Development
- Pablo Lozano (arkadis)
- quentin neyrat (qneyrat)
- - Dane Powell
- Antonio Jose Cerezo (ajcerezo)
- Marcin Szepczynski (czepol)
- Lescot Edouard (idetox)
- Loïc Frémont (loic425)
- Rob Frawley 2nd (robfrawley)
- Mohammad Emran Hasan (phpfour)
- - Allison Guilhem (a_guilhem)
- Dmitriy Mamontov (mamontovdmitriy)
- Kévin THERAGE (kevin_therage)
- Nikita Konstantinov (unkind)
@@ -430,7 +431,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Francois Zaninotto
- Laurent Masforné (heisenberg)
- Claude Khedhiri (ck-developer)
- - Giorgio Premi
- Daniel Tschinder
- Christian Schmidt
- Alexander Kotynia (olden)
@@ -576,6 +576,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Grégoire Passault (gregwar)
- Jerzy Zawadzki (jzawadzki)
- Ismael Ambrosi (iambrosi)
+ - Samaël Villette (samadu61)
- Saif Eddin G
- Emmanuel BORGES (eborges78)
- siganushka (siganushka)
@@ -691,6 +692,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ruben Jacobs (rubenj)
- Arkadius Stefanski (arkadius)
- Jérémy M (th3mouk)
+ - Tristan Pouliquen
- Terje Bråten
- Pierre Rineau
- Renan Gonçalves (renan_saddam)
@@ -720,6 +722,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Eric COURTIAL
- Xesxen
- ShinDarth
+ - Phil E. Taylor (philetaylor)
- Arun Philip
- Stéphane PY (steph_py)
- Philipp Kräutli (pkraeutli)
@@ -752,10 +755,10 @@ The Symfony Connect username in parenthesis allows to get more information
- Hassan Amouhzi
- Antonin CLAUZIER (0x346e3730)
- Andrei C. (moldman)
- - Samaël Villette (samadu61)
- Tamas Szijarto
- stlrnz
- Adrien Wilmet (adrienfr)
+ - Mathieu Rochette (mathroc)
- Alex Bacart
- hugovms
- Michele Locati
@@ -898,6 +901,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Jonas Flodén (flojon)
- Adrien Lucas (adrienlucas)
- Dominik Zogg
+ - Quentin Devos
- Kai Dederichs
- Luc Vieillescazes (iamluc)
- Thomas Nunninger
@@ -956,7 +960,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Gigino Chianese (sajito)
- Xav` (xavismeh)
- Remi Collet
- - Mathieu Rochette (mathroc)
- Vicent Soria Durá (vicentgodella)
- Michael Moravec
- Anthony Ferrara
@@ -1026,6 +1029,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Loïc Faugeron
- Aurélien Fredouelle
- Pavel Campr (pcampr)
+ - Markus Staab
- Forfarle (forfarle)
- Johnny Robeson (johnny)
- Kai Eichinger (kai_eichinger)
@@ -1267,7 +1271,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Szijarto Tamas
- Arend Hummeling
- Makdessi Alex
- - Phil E. Taylor (philetaylor)
- Juan Miguel Besada Vidal (soutlink)
- dlorek
- Stuart Fyfe
@@ -1295,7 +1298,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Simon Schick (simonsimcity)
- Victor Macko (victor_m)
- Tristan Roussel
- - Quentin Devos
- Jorge Vahldick (jvahldick)
- Vladimir Mantulo (mantulo)
- aim8604
@@ -1303,6 +1305,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Maciej Zgadzaj
- David Legatt (dlegatt)
- Maarten de Boer (mdeboer)
+ - Alexandre parent
- Cameron Porter
- Hossein Bukhamsin
- Oliver Hoff
@@ -1449,7 +1452,6 @@ The Symfony Connect username in parenthesis allows to get more information
- Michael Olšavský
- Benny Born
- Emirald Mateli
- - Tristan Pouliquen
- Jose Gonzalez
- Claudio Zizza
- Ivo Valchev
@@ -1841,7 +1843,6 @@ The Symfony Connect username in parenthesis allows to get more information
- vladyslavstartsev
- Kévin
- Marc Abramowitz
- - Markus Staab
- michal
- Martijn Evers
- Sjoerd Adema
@@ -2220,11 +2221,13 @@ The Symfony Connect username in parenthesis allows to get more information
- Andrew Tch
- Alexander Cheprasov
- Rodrigo Díez Villamuera (rodrigodiez)
+ - Brad Treloar
- Stephen Clouse
- e-ivanov
- Abderrahman DAIF (death_maker)
- Yann Rabiller (einenlum)
- Jochen Bayer (jocl)
+ - Constantine Shtompel
- VAN DER PUTTE Guillaume (guillaume_vdp)
- Patrick Carlo-Hickman
- Bruno MATEU
@@ -2235,6 +2238,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Viacheslav Sychov
- Nicolas Sauveur (baishu)
- Helmut Hummel (helhum)
+ - Andrew Neil Forster (krciga22)
- Matt Brunt
- Carlos Ortega Huetos
- Péter Buri (burci)
@@ -2285,7 +2289,6 @@ The Symfony Connect username in parenthesis allows to get more information
- John Espiritu (johnillo)
- Oxan van Leeuwen
- pkowalczyk
- - Alexandre parent
- Soner Sayakci
- Max Voloshin (maxvoloshin)
- Nicolas Fabre (nfabre)
@@ -2400,6 +2403,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Claudiu Cristea
- Zacharias Luiten
- Sebastian Utz
+ - Oliver Hader
- Adrien Gallou (agallou)
- Maks Rafalko (bornfree)
- Conrad Kleinespel (conradk)
@@ -2417,6 +2421,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Cédric Lahouste (rapotor)
- Samuel Vogel (samuelvogel)
- Berat Doğan
+ - Christian Kolb
- Guillaume LECERF
- Juanmi Rodriguez Cerón
- twifty
@@ -2437,6 +2442,8 @@ The Symfony Connect username in parenthesis allows to get more information
- Eric Stern
- ShiraNai7
- Antal Áron (antalaron)
+ - Alexander Grimalovsky (flying)
+ - Ivan Pepelko (pepelko)
- Vašek Purchart (vasek-purchart)
- Janusz Jabłoński (yanoosh)
- Fleuv
@@ -2581,6 +2588,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Nicolas Schwartz (nicoschwartz)
- Tim Jabs (rubinum)
- Stéphane Seng (stephaneseng)
+ - Robert Korulczyk
- Jonathan Gough
- Benoit Leveque
- Benjamin Bender
@@ -2652,6 +2660,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Martin Schophaus (m_schophaus_adcada)
- Martynas Sudintas (martiis)
- Anton Sukhachev (mrsuh)
+ - Vitaliy Zhuk (zhukv)
- Marcel Siegert
- ryunosuke
- Roy de Vos Burchart
@@ -2707,6 +2716,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Jovan Perovic (jperovic)
- Pablo Maria Martelletti (pmartelletti)
- Sander van der Vlugt (stranding)
+ - Maxim Tugaev (tugmaks)
- Florian Bogey
- Waqas Ahmed
- Bert Hekman
@@ -3141,6 +3151,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Antoine LA
- Vyacheslav Slinko
- Benjamin Laugueux
+ - Lane Shukhov
- Jakub Chábek
- William Pinaud (DocFX)
- Johannes
@@ -3244,6 +3255,7 @@ The Symfony Connect username in parenthesis allows to get more information
- Ahmed Abdulrahman
- dinitrol
- Penny Leach
+ - Kevin Mian Kraiker
- Yurii K
- Richard Trebichavský
- g123456789l
@@ -3282,6 +3294,7 @@ The Symfony Connect username in parenthesis allows to get more information
- ADmad
- Nicolas Roudaire
- Abdouni Karim (abdounikarim)
+ - Adrian Günter (adrianguenter)
- Andreas Forsblom (aforsblo)
- Alex Olmos (alexolmos)
- Cedric BERTOLINI (alsciende)
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
index f28933cf97357..50f62b9abcfef 100644
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
@@ -151,6 +151,11 @@
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
}
+if (!$getEnvVar('DOCTRINE_DEPRECATIONS')) {
+ putenv('DOCTRINE_DEPRECATIONS=trigger');
+ $_SERVER['DOCTRINE_DEPRECATIONS'] = $_ENV['DOCTRINE_DEPRECATIONS'] = 'trigger';
+}
+
$COMPOSER = ($COMPOSER = getenv('COMPOSER_BINARY'))
|| file_exists($COMPOSER = $oldPwd.'/composer.phar')
|| ($COMPOSER = rtrim((string) ('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', shell_exec('where.exe composer.phar 2> NUL')) : shell_exec('which composer.phar 2> /dev/null'))))
diff --git a/src/Symfony/Bridge/Twig/Extension/ImportMapRuntime.php b/src/Symfony/Bridge/Twig/Extension/ImportMapRuntime.php
index aa5097d35de5d..aa68111b7b819 100644
--- a/src/Symfony/Bridge/Twig/Extension/ImportMapRuntime.php
+++ b/src/Symfony/Bridge/Twig/Extension/ImportMapRuntime.php
@@ -22,8 +22,8 @@ public function __construct(private readonly ImportMapRenderer $importMapRendere
{
}
- public function importmap(?string $entryPoint = 'app'): string
+ public function importmap(?string $entryPoint = 'app', array $attributes = []): string
{
- return $this->importMapRenderer->render($entryPoint);
+ return $this->importMapRenderer->render($entryPoint, $attributes);
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
index 09e975ad4a3d7..332e0eccd7b9d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php
@@ -106,6 +106,9 @@ protected function describeContainerServices(ContainerBuilder $container, array
if ($service instanceof Alias) {
$data['aliases'][$serviceId] = $this->getContainerAliasData($service);
} elseif ($service instanceof Definition) {
+ if ($service->hasTag('container.excluded')) {
+ continue;
+ }
$data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments, $container, $serviceId);
} else {
$data['services'][$serviceId] = $service::class;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
index 1289c8ded9303..7423a28550c90 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php
@@ -162,6 +162,9 @@ protected function describeContainerServices(ContainerBuilder $container, array
if ($service instanceof Alias) {
$services['aliases'][$serviceId] = $service;
} elseif ($service instanceof Definition) {
+ if ($service->hasTag('container.excluded')) {
+ continue;
+ }
$services['definitions'][$serviceId] = $service;
} else {
$services['services'][$serviceId] = $service;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
index 519d99f3a97cd..06afc5ceacdc9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php
@@ -198,6 +198,10 @@ protected function describeContainerServices(ContainerBuilder $container, array
}
if ($definition instanceof Definition) {
+ if ($definition->hasTag('container.excluded')) {
+ unset($serviceIds[$key]);
+ continue;
+ }
if ($showTag) {
$tags = $definition->getTag($showTag);
foreach ($tags as $tag) {
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
index 79253d53f1b5f..7c03aeba1301a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php
@@ -293,6 +293,10 @@ private function getContainerServicesDocument(ContainerBuilder $container, strin
continue;
}
+ if ($service instanceof Definition && $service->hasTag('container.excluded')) {
+ continue;
+ }
+
$serviceXML = $this->getContainerServiceDocument($service, $serviceId, null, $showArguments);
$containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true));
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
index 6d39e9440f02a..13e155235358b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
@@ -14,7 +14,6 @@
use Psr\Container\ContainerInterface;
use Psr\Link\EvolvableLinkInterface;
use Psr\Link\LinkInterface;
-use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
@@ -97,7 +96,6 @@ public static function getSubscribedServices(): array
'security.csrf.token_manager' => '?'.CsrfTokenManagerInterface::class,
'parameter_bag' => '?'.ContainerBagInterface::class,
'web_link.http_header_serializer' => '?'.HttpHeaderSerializer::class,
- 'asset_mapper.importmap.manager' => '?'.ImportMapManager::class,
];
}
@@ -412,7 +410,7 @@ protected function addLink(Request $request, LinkInterface $link): void
/**
* @param LinkInterface[] $links
*/
- protected function sendEarlyHints(iterable $links = [], Response $response = null, bool $preloadJavaScriptModules = false): Response
+ protected function sendEarlyHints(iterable $links = [], Response $response = null): Response
{
if (!$this->container->has('web_link.http_header_serializer')) {
throw new \LogicException('You cannot use the "sendEarlyHints" method if the WebLink component is not available. Try running "composer require symfony/web-link".');
@@ -421,17 +419,6 @@ protected function sendEarlyHints(iterable $links = [], Response $response = nul
$response ??= new Response();
$populatedLinks = [];
-
- if ($preloadJavaScriptModules) {
- if (!$this->container->has('asset_mapper.importmap.manager')) {
- throw new \LogicException('You cannot use the JavaScript modules method if the AssetMapper component is not available. Try running "composer require symfony/asset-mapper".');
- }
-
- foreach ($this->container->get('asset_mapper.importmap.manager')->getModulesToPreload() as $url) {
- $populatedLinks[] = new Link('modulepreload', $url);
- }
- }
-
foreach ($links as $link) {
if ($link instanceof EvolvableLinkInterface && !$link->getRels()) {
$link = $link->withRel('preload');
diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
index ffb96a23e5f5b..36e5cd4028010 100644
--- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
@@ -95,6 +95,8 @@ class FrameworkBundle extends Bundle
*/
public function boot()
{
+ $_ENV['DOCTRINE_DEPRECATIONS'] = $_SERVER['DOCTRINE_DEPRECATIONS'] ??= 'trigger';
+
$handler = ErrorHandler::register(null, false);
$this->container->get('debug.error_handler_configurator')->configure($handler);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php
index 960735e884958..efa9c7becab59 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php
@@ -74,7 +74,6 @@ public function testSubscribedServices()
'security.token_storage' => '?Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorageInterface',
'security.csrf.token_manager' => '?Symfony\\Component\\Security\\Csrf\\CsrfTokenManagerInterface',
'web_link.http_header_serializer' => '?Symfony\\Component\\WebLink\\HttpHeaderSerializer',
- 'asset_mapper.importmap.manager' => '?Symfony\\Component\\AssetMapper\\ImportMap\\ImportMapManager',
];
$this->assertEquals($expectedServices, $subscribed, 'Subscribed core services in AbstractController have changed');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php
new file mode 100644
index 0000000000000..2508b74276bc0
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php
@@ -0,0 +1,7 @@
+assertStringContainsString('[WARNING] The "deprecated_alias" alias is deprecated since foo/bar 1.9 and will be removed in 2.0', $tester->getDisplay());
}
+ public function testExcludedService()
+ {
+ static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']);
+
+ $application = new Application(static::$kernel);
+ $application->setAutoExit(false);
+
+ $tester = new ApplicationTester($application);
+
+ $tester->run(['command' => 'debug:container']);
+ $this->assertStringNotContainsString(ContainerExcluded::class, $tester->getDisplay());
+ }
+
/**
* @dataProvider provideIgnoreBackslashWhenFindingService
*/
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
index cc1a01bb8f0b5..15ddaca64356f 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
@@ -33,3 +33,5 @@ services:
- '%env(REAL)%'
- '%env(int:key:2:json:JSON)%'
- '%env(float:key:2:json:JSON)%'
+ Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ContainerExcluded:
+ tags: ['container.excluded']
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php
index 6f19f3845cb15..18a5ee6ac3eef 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php
@@ -13,7 +13,6 @@
use Jose\Component\Core\Algorithm;
use Jose\Component\Core\JWK;
-use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -28,26 +27,32 @@ class OidcTokenHandlerFactory implements TokenHandlerFactoryInterface
{
public function create(ContainerBuilder $container, string $id, array|string $config): void
{
- $tokenHandlerDefinition = $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc'));
- $tokenHandlerDefinition->replaceArgument(3, $config['claim']);
- $tokenHandlerDefinition->replaceArgument(4, $config['audience']);
+ $tokenHandlerDefinition = $container->setDefinition($id, (new ChildDefinition('security.access_token_handler.oidc'))
+ ->replaceArgument(2, $config['audience'])
+ ->replaceArgument(3, $config['issuers'])
+ ->replaceArgument(4, $config['claim'])
+ );
- // Create the signature algorithm and the JWK
if (!ContainerBuilder::willBeAvailable('web-token/jwt-core', Algorithm::class, ['symfony/security-bundle'])) {
- $container->register('security.access_token_handler.oidc.signature', 'stdClass')
+ $container->register('security.access_token_handler.oidc.signature', Algorithm::class)
->addError('You cannot use the "oidc" token handler since "web-token/jwt-core" is not installed. Try running "web-token/jwt-core".');
- $container->register('security.access_token_handler.oidc.jwk', 'stdClass')
+ $container->register('security.access_token_handler.oidc.jwk', JWK::class)
->addError('You cannot use the "oidc" token handler since "web-token/jwt-core" is not installed. Try running "web-token/jwt-core".');
+ }
+
+ // @see Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory
+ // for supported algorithms
+ if (\in_array($config['algorithm'], ['ES256', 'ES384', 'ES512'], true)) {
+ $tokenHandlerDefinition->replaceArgument(0, new Reference('security.access_token_handler.oidc.signature.'.$config['algorithm']));
} else {
- $container->register('security.access_token_handler.oidc.signature', Algorithm::class)
- ->setFactory([SignatureAlgorithmFactory::class, 'create'])
- ->setArguments([$config['signature']['algorithm']]);
- $container->register('security.access_token_handler.oidc.jwk', JWK::class)
- ->setFactory([JWK::class, 'createFromJson'])
- ->setArguments([$config['signature']['key']]);
+ $tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature'))
+ ->replaceArgument(0, $config['algorithm'])
+ );
}
- $tokenHandlerDefinition->replaceArgument(0, new Reference('security.access_token_handler.oidc.signature'));
- $tokenHandlerDefinition->replaceArgument(1, new Reference('security.access_token_handler.oidc.jwk'));
+
+ $tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwk'))
+ ->replaceArgument(0, $config['key'])
+ );
}
public function getKey(): string
@@ -67,20 +72,20 @@ public function addConfiguration(NodeBuilder $node): void
->end()
->scalarNode('audience')
->info('Audience set in the token, for validation purpose.')
- ->defaultNull()
+ ->isRequired()
+ ->end()
+ ->arrayNode('issuers')
+ ->info('Issuers allowed to generate the token, for validation purpose.')
+ ->isRequired()
+ ->prototype('scalar')->end()
+ ->end()
+ ->scalarNode('algorithm')
+ ->info('Algorithm used to sign the token.')
+ ->isRequired()
->end()
- ->arrayNode('signature')
+ ->scalarNode('key')
+ ->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
->isRequired()
- ->children()
- ->scalarNode('algorithm')
- ->info('Algorithm used to sign the token.')
- ->isRequired()
- ->end()
- ->scalarNode('key')
- ->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
- ->isRequired()
- ->end()
- ->end()
->end()
->end()
->end()
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php
index 08b1019f2c210..78bc45224db33 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php
@@ -15,7 +15,7 @@
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\HttpClient\HttpClient;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* Configures a token handler for an OIDC server.
@@ -26,24 +26,20 @@ class OidcUserInfoTokenHandlerFactory implements TokenHandlerFactoryInterface
{
public function create(ContainerBuilder $container, string $id, array|string $config): void
{
- $tokenHandlerDefinition = $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc_user_info'));
- $tokenHandlerDefinition->replaceArgument(2, $config['claim']);
+ $clientDefinition = (new ChildDefinition('security.access_token_handler.oidc_user_info.http_client'))
+ ->replaceArgument(0, ['base_uri' => $config['base_uri']]);
- // Create the client service
- if (!isset($config['client']['id'])) {
- $clientDefinitionId = 'http_client.security.access_token_handler.oidc_user_info';
- if (!ContainerBuilder::willBeAvailable('symfony/http-client', HttpClient::class, ['symfony/security-bundle'])) {
- $container->register($clientDefinitionId, 'stdClass')
- ->addError('You cannot use the "oidc_user_info" token handler since the HttpClient component is not installed. Try running "composer require symfony/http-client".');
- } else {
- $container->register($clientDefinitionId, HttpClient::class)
- ->setFactory([HttpClient::class, 'create'])
- ->setArguments([$config['client']])
- ->addTag('http_client.client');
- }
+ if (isset($config['client'])) {
+ $clientDefinition->setFactory([new Reference($config['client']), 'withOptions']);
+ } elseif (!ContainerBuilder::willBeAvailable('symfony/http-client', HttpClientInterface::class, ['symfony/security-bundle'])) {
+ $clientDefinition
+ ->setFactory(null)
+ ->addError('You cannot use the "oidc_user_info" token handler since the HttpClient component is not installed. Try running "composer require symfony/http-client".');
}
- $tokenHandlerDefinition->replaceArgument(0, new Reference($config['client']['id'] ?? $clientDefinitionId));
+ $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc_user_info'))
+ ->replaceArgument(0, $clientDefinition)
+ ->replaceArgument(2, $config['claim']);
}
public function getKey(): string
@@ -56,19 +52,23 @@ public function addConfiguration(NodeBuilder $node): void
$node
->arrayNode($this->getKey())
->fixXmlConfig($this->getKey())
+ ->beforeNormalization()
+ ->ifString()
+ ->then(static fn ($v) => ['claim' => 'sub', 'base_uri' => $v])
+ ->end()
->children()
+ ->scalarNode('base_uri')
+ ->info('Base URI of the userinfo endpoint on the OIDC server.')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
->scalarNode('claim')
- ->info('Claim which contains the user identifier (e.g.: sub, email..).')
+ ->info('Claim which contains the user identifier (e.g. sub, email, etc.).')
->defaultValue('sub')
+ ->cannotBeEmpty()
->end()
- ->arrayNode('client')
- ->info('HttpClient to call the OIDC server.')
- ->isRequired()
- ->beforeNormalization()
- ->ifString()
- ->then(static function ($v): array { return ['id' => $v]; })
- ->end()
- ->prototype('scalar')->end()
+ ->scalarNode('client')
+ ->info('HttpClient service id to use to call the OIDC server.')
->end()
->end()
->end()
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php
index f9f876deff2bf..a81d662cd338c 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php
@@ -11,7 +11,7 @@
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
-use Jose\Component\Core\Algorithm as SignatureAlgorithm;
+use Jose\Component\Core\Algorithm as AlgorithmInterface;
use Jose\Component\Signature\Algorithm;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Http\AccessToken\Oidc\OidcTokenHandler;
@@ -23,29 +23,21 @@
*/
final class SignatureAlgorithmFactory
{
- public static function create(string $algorithm): SignatureAlgorithm
+ public static function create(string $algorithm): AlgorithmInterface
{
switch ($algorithm) {
case 'ES256':
- if (!class_exists(Algorithm\ES256::class)) {
- throw new \LogicException('You cannot use the "ES256" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".');
- }
-
- return new Algorithm\ES256();
case 'ES384':
- if (!class_exists(Algorithm\ES384::class)) {
- throw new \LogicException('You cannot use the "ES384" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".');
- }
-
- return new Algorithm\ES384();
case 'ES512':
- if (!class_exists(Algorithm\ES512::class)) {
- throw new \LogicException('You cannot use the "ES512" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".');
+ if (!class_exists(Algorithm::class.'\\'.$algorithm)) {
+ throw new \LogicException(sprintf('You cannot use the "%s" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".', $algorithm));
}
- return new Algorithm\ES512();
- default:
- throw new InvalidArgumentException(sprintf('Unsupported signature algorithm "%s". Only ES* algorithms are supported. If you want to use another algorithm, create your TokenHandler as a service.', $algorithm));
+ $algorithm = Algorithm::class.'\\'.$algorithm;
+
+ return new $algorithm();
}
+
+ throw new InvalidArgumentException(sprintf('Unsupported signature algorithm "%s". Only ES* algorithms are supported. If you want to use another algorithm, create your TokenHandler as a service.', $algorithm));
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd
index 23fe45e243fbf..ab90f4b42606c 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd
@@ -309,6 +309,7 @@
+
@@ -317,6 +318,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php
index fafe477d5bd23..3254f332aace0 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php
@@ -11,6 +11,12 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+use Jose\Component\Core\Algorithm;
+use Jose\Component\Core\JWK;
+use Jose\Component\Signature\Algorithm\ES256;
+use Jose\Component\Signature\Algorithm\ES384;
+use Jose\Component\Signature\Algorithm\ES512;
+use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory;
use Symfony\Component\Security\Http\AccessToken\ChainAccessTokenExtractor;
use Symfony\Component\Security\Http\AccessToken\FormEncodedBodyExtractor;
use Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor;
@@ -18,6 +24,7 @@
use Symfony\Component\Security\Http\AccessToken\Oidc\OidcUserInfoTokenHandler;
use Symfony\Component\Security\Http\AccessToken\QueryAccessTokenExtractor;
use Symfony\Component\Security\Http\Authenticator\AccessTokenAuthenticator;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
return static function (ContainerConfigurator $container) {
$container->services()
@@ -44,22 +51,55 @@
])
// OIDC
+ ->set('security.access_token_handler.oidc_user_info.http_client', HttpClientInterface::class)
+ ->abstract()
+ ->factory([service('http_client'), 'withOptions'])
+ ->args([abstract_arg('http client options')])
+
->set('security.access_token_handler.oidc_user_info', OidcUserInfoTokenHandler::class)
->abstract()
->args([
abstract_arg('http client'),
service('logger')->nullOnInvalid(),
- 'sub',
+ abstract_arg('claim'),
])
->set('security.access_token_handler.oidc', OidcTokenHandler::class)
->abstract()
->args([
abstract_arg('signature algorithm'),
- abstract_arg('jwk'),
- service('logger')->nullOnInvalid(),
+ abstract_arg('signature key'),
+ abstract_arg('audience'),
+ abstract_arg('issuers'),
'sub',
- null,
+ service('logger')->nullOnInvalid(),
+ service('clock'),
+ ])
+
+ ->set('security.access_token_handler.oidc.jwk', JWK::class)
+ ->abstract()
+ ->factory([JWK::class, 'createFromJson'])
+ ->args([
+ abstract_arg('signature key'),
])
+
+ ->set('security.access_token_handler.oidc.signature', Algorithm::class)
+ ->abstract()
+ ->factory([SignatureAlgorithmFactory::class, 'create'])
+ ->args([
+ abstract_arg('signature algorithm'),
+ ])
+
+ ->set('security.access_token_handler.oidc.signature.ES256', ES256::class)
+ ->parent('security.access_token_handler.oidc.signature')
+ ->args(['index_0' => 'ES256'])
+
+ ->set('security.access_token_handler.oidc.signature.ES384', ES384::class)
+ ->parent('security.access_token_handler.oidc.signature')
+ ->args(['index_0' => 'ES384'])
+
+ ->set('security.access_token_handler.oidc.signature.ES512', ES512::class)
+ ->parent('security.access_token_handler.oidc.signature')
+ ->args(['index_0' => 'ES512'])
;
};
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php
index a9da80fbb40ba..4607830e73916 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php
@@ -18,7 +18,10 @@
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AccessTokenFactory;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
+use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
class AccessTokenFactoryTest extends TestCase
{
@@ -76,7 +79,12 @@ public function testOidcUserInfoTokenHandlerConfigurationWithExistingClient()
{
$container = new ContainerBuilder();
$config = [
- 'token_handler' => ['oidc_user_info' => ['client' => 'oidc.client']],
+ 'token_handler' => [
+ 'oidc_user_info' => [
+ 'base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo',
+ 'client' => 'oidc.client',
+ ],
+ ],
];
$factory = new AccessTokenFactory($this->createTokenHandlerFactories());
@@ -86,14 +94,24 @@ public function testOidcUserInfoTokenHandlerConfigurationWithExistingClient()
$this->assertTrue($container->hasDefinition('security.authenticator.access_token.firewall1'));
$this->assertTrue($container->hasDefinition('security.access_token_handler.firewall1'));
- $this->assertFalse($container->hasDefinition('http_client.security.access_token_handler.oidc_user_info'));
+
+ $expected = [
+ 'index_0' => (new ChildDefinition('security.access_token_handler.oidc_user_info.http_client'))
+ ->setFactory([new Reference('oidc.client'), 'withOptions'])
+ ->replaceArgument(0, ['base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo']),
+ 'index_2' => 'sub',
+ ];
+ $this->assertEquals($expected, $container->getDefinition('security.access_token_handler.firewall1')->getArguments());
}
- public function testOidcUserInfoTokenHandlerConfigurationWithClientCreation()
+ /**
+ * @dataProvider getOidcUserInfoConfiguration
+ */
+ public function testOidcUserInfoTokenHandlerConfigurationWithBaseUri(array|string $configuration)
{
$container = new ContainerBuilder();
$config = [
- 'token_handler' => ['oidc_user_info' => ['client' => ['base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo']]],
+ 'token_handler' => ['oidc_user_info' => $configuration],
];
$factory = new AccessTokenFactory($this->createTokenHandlerFactories());
@@ -103,7 +121,26 @@ public function testOidcUserInfoTokenHandlerConfigurationWithClientCreation()
$this->assertTrue($container->hasDefinition('security.authenticator.access_token.firewall1'));
$this->assertTrue($container->hasDefinition('security.access_token_handler.firewall1'));
- $this->assertTrue($container->hasDefinition('http_client.security.access_token_handler.oidc_user_info'));
+
+ $expected = [
+ 'index_0' => (new ChildDefinition('security.access_token_handler.oidc_user_info.http_client'))
+ ->replaceArgument(0, ['base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo']),
+ 'index_2' => 'sub',
+ ];
+
+ if (!interface_exists(HttpClientInterface::class)) {
+ $expected['index_0']
+ ->setFactory(null)
+ ->addError('You cannot use the "oidc_user_info" token handler since the HttpClient component is not installed. Try running "composer require symfony/http-client".');
+ }
+
+ $this->assertEquals($expected, $container->getDefinition('security.access_token_handler.firewall1')->getArguments());
+ }
+
+ public static function getOidcUserInfoConfiguration(): iterable
+ {
+ yield [['base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo']];
+ yield ['https://www.example.com/realms/demo/protocol/openid-connect/userinfo'];
}
public function testMultipleTokenHandlersSet()
@@ -114,7 +151,7 @@ public function testMultipleTokenHandlersSet()
$config = [
'token_handler' => [
'id' => 'in_memory_token_handler_service_id',
- 'oidc_user_info' => ['client' => 'oidc.client'],
+ 'oidc_user_info' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo',
],
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php
index 8ca49ccc1430c..3deb91402165e 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php
@@ -343,7 +343,7 @@ public function testOidcSuccess()
'iat' => $time,
'nbf' => $time,
'exp' => $time + 3600,
- 'iss' => 'https://www.example.com/',
+ 'iss' => 'https://www.example.com',
'aud' => 'Symfony OIDC',
'sub' => 'e21bf182-1538-406e-8ccb-e25a17aba39f',
'username' => 'dunglas',
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml
index 45802961a1a61..dd770e4520e41 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml
@@ -23,10 +23,10 @@ security:
oidc:
claim: 'username'
audience: 'Symfony OIDC'
- signature:
- algorithm: 'ES256'
- # tip: use https://mkjwk.org/ to generate a JWK
- key: '{"kty":"EC","d":"iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220","crv":"P-256","x":"0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4","y":"KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo"}'
+ issuers: [ 'https://www.example.com' ]
+ algorithm: 'ES256'
+ # tip: use https://mkjwk.org/ to generate a JWK
+ key: '{"kty":"EC","d":"iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220","crv":"P-256","x":"0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4","y":"KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo"}'
token_extractors: 'header'
realm: 'My API'
diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json
index 8fb916cd27114..c10e76ca459bb 100644
--- a/src/Symfony/Bundle/SecurityBundle/composer.json
+++ b/src/Symfony/Bundle/SecurityBundle/composer.json
@@ -19,6 +19,7 @@
"php": ">=8.1",
"composer-runtime-api": ">=2.1",
"ext-xml": "*",
+ "symfony/clock": "^6.3",
"symfony/config": "^6.1",
"symfony/dependency-injection": "^6.2",
"symfony/event-dispatcher": "^5.4|^6.0",
@@ -38,7 +39,7 @@
"symfony/dom-crawler": "^5.4|^6.0",
"symfony/expression-language": "^5.4|^6.0",
"symfony/form": "^5.4|^6.0",
- "symfony/framework-bundle": "^5.4|^6.0",
+ "symfony/framework-bundle": "^6.3",
"symfony/ldap": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/rate-limiter": "^5.4|^6.0",
@@ -59,7 +60,8 @@
"conflict": {
"symfony/browser-kit": "<5.4",
"symfony/console": "<5.4",
- "symfony/framework-bundle": "<5.4",
+ "symfony/framework-bundle": "<6.3",
+ "symfony/http-client": "<5.4",
"symfony/ldap": "<5.4",
"symfony/twig-bundle": "<5.4"
},
diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php
index 7a5dc43001001..2a9a215f38a63 100644
--- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php
+++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php
@@ -29,15 +29,16 @@ public function __construct(
) {
}
- public function render(string $entryPoint = null): string
+ public function render(string $entryPoint = null, array $attributes = []): string
{
$attributeString = '';
- if (isset($this->scriptAttributes['src']) || isset($this->scriptAttributes['type'])) {
+ $attributes += $this->scriptAttributes;
+ if (isset($attributes['src']) || isset($attributes['type'])) {
throw new \InvalidArgumentException(sprintf('The "src" and "type" attributes are not allowed on the ";
+ $output .= "\n";
}
return $output;
diff --git a/src/Symfony/Component/Console/Output/AnsiColorMode.php b/src/Symfony/Component/Console/Output/AnsiColorMode.php
index f1ca5826381cb..5f9f744fe61fe 100644
--- a/src/Symfony/Component/Console/Output/AnsiColorMode.php
+++ b/src/Symfony/Component/Console/Output/AnsiColorMode.php
@@ -26,7 +26,7 @@ enum AnsiColorMode
case Ansi4;
/*
- * 8-bit Ansi colors (240 differents colors + 16 duplicate color codes, ensuring backward compatibility).
+ * 8-bit Ansi colors (240 different colors + 16 duplicate color codes, ensuring backward compatibility).
* Output syntax is: "ESC[38;5;${foreGroundColorcode};48;5;${backGroundColorcode}m"
* Should be compatible with most terminals.
*/
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
index 9d9250b9f9404..f84a7faff07ec 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
@@ -55,6 +55,15 @@ public function __construct(bool $throwOnAutowireException = true)
$this->defaultArgument = new class() {
public $value;
public $names;
+ public $bag;
+
+ public function withValue(\ReflectionParameter $parameter): self
+ {
+ $clone = clone $this;
+ $clone->value = $this->bag->escapeValue($parameter->getDefaultValue());
+
+ return $clone;
+ }
};
}
@@ -63,6 +72,8 @@ public function __construct(bool $throwOnAutowireException = true)
*/
public function process(ContainerBuilder $container)
{
+ $this->defaultArgument->bag = $container->getParameterBag();
+
try {
$this->typesClone = clone $this;
parent::process($container);
@@ -70,6 +81,7 @@ public function process(ContainerBuilder $container)
$this->decoratedClass = null;
$this->decoratedId = null;
$this->methodCalls = null;
+ $this->defaultArgument->bag = null;
$this->defaultArgument->names = null;
$this->getPreviousValue = null;
$this->decoratedMethodIndex = null;
@@ -275,8 +287,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
$failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
if ($parameter->isDefaultValueAvailable()) {
- $value = clone $this->defaultArgument;
- $value->value = $parameter->getDefaultValue();
+ $value = $this->defaultArgument->withValue($parameter);
} elseif (!$parameter->allowsNull()) {
throw new AutowiringFailedException($this->currentId, $failureMessage);
}
@@ -374,8 +385,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
}
// specifically pass the default value
- $arguments[$index] = clone $this->defaultArgument;
- $arguments[$index]->value = $parameter->getDefaultValue();
+ $arguments[$index] = $this->defaultArgument->withValue($parameter);
continue;
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
index 72b45c7a02246..1b507baa50363 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
@@ -1284,6 +1284,19 @@ public function testAutowireWithNamedArgs()
$this->assertEquals([new TypedReference(A::class, A::class), 'abc'], $container->getDefinition('foo')->getArguments());
}
+ public function testAutowireDefaultValueParametersLike()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', ParametersLikeDefaultValue::class)
+ ->setAutowired(true)
+ ->setArgument(1, 'ok');
+
+ (new AutowirePass())->process($container);
+
+ $this->assertSame('%%not%%one%%parameter%%here%%', $container->getDefinition('foo')->getArgument(0));
+ }
+
public function testAutowireAttribute()
{
$container = new ContainerBuilder();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
index 8e284247a61c8..31eb9d9bb68a5 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php
@@ -528,6 +528,13 @@ public function __construct(NotExisting $notExisting)
}
}
+class ParametersLikeDefaultValue
+{
+ public function __construct(string $parameterLike = '%not%one%parameter%here%', string $willBeSetToKeepFirstArgumentDefaultValue = 'ok')
+ {
+ }
+}
+
class StaticConstructor
{
public function __construct(private string $bar)
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index d3280bee51fa9..a379ce1863100 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -664,6 +664,12 @@ public function dumpFile(string $filename, $content)
$dir = \dirname($filename);
+ if (is_link($filename) && $linkTarget = $this->readlink($filename)) {
+ $this->dumpFile(Path::makeAbsolute($linkTarget, $dir), $content);
+
+ return;
+ }
+
if (!is_dir($dir)) {
$this->mkdir($dir);
}
diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
index 7aca5dd25adbd..5fb7f4400c8c9 100644
--- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
+++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
@@ -1087,14 +1087,12 @@ public function testReadBrokenLink()
{
$this->markAsSkippedIfSymlinkIsMissing();
- if ('\\' === \DIRECTORY_SEPARATOR) {
- $this->markTestSkipped('Windows does not support creating "broken" symlinks');
- }
-
$file = $this->workspace.'/file';
$link = $this->workspace.'/link';
+ touch($file);
$this->filesystem->symlink($file, $link);
+ $this->filesystem->remove($file);
$this->assertEquals($file, $this->filesystem->readlink($link));
$this->assertNull($this->filesystem->readlink($link, true));
@@ -1601,6 +1599,33 @@ public function testDumpFileOverwritesAnExistingFile()
$this->assertStringEqualsFile($filename, 'bar');
}
+ public function testDumpFileFollowsSymlink()
+ {
+ $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt';
+ file_put_contents($filename, 'FOO BAR');
+ $linknameA = $this->workspace.\DIRECTORY_SEPARATOR.'bar.txt';
+ $linknameB = $this->workspace.\DIRECTORY_SEPARATOR.'baz.txt';
+ $this->filesystem->symlink($filename, $linknameA);
+ $this->filesystem->symlink($linknameA, $linknameB);
+
+ $this->filesystem->dumpFile($linknameB, 'bar');
+
+ $this->assertFileExists($filename);
+ $this->assertFileExists($linknameA);
+ $this->assertFileExists($linknameB);
+ $this->assertStringEqualsFile($filename, 'bar');
+ $this->assertStringEqualsFile($linknameA, 'bar');
+ $this->assertStringEqualsFile($linknameB, 'bar');
+
+ $this->filesystem->remove($filename);
+ $this->filesystem->dumpFile($linknameB, 'baz');
+
+ $this->assertFileExists($filename);
+ $this->assertStringEqualsFile($filename, 'baz');
+ $this->assertStringEqualsFile($linknameA, 'baz');
+ $this->assertStringEqualsFile($linknameB, 'baz');
+ }
+
public function testDumpFileWithFileScheme()
{
$scheme = 'file://';
@@ -1674,6 +1699,35 @@ public function testAppendToFileWithResource()
}
}
+ public function testAppendToFileFollowsSymlink()
+ {
+ $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt';
+ file_put_contents($filename, 'foo');
+ $linknameA = $this->workspace.\DIRECTORY_SEPARATOR.'bar.txt';
+ $linknameB = $this->workspace.\DIRECTORY_SEPARATOR.'baz.txt';
+ $this->filesystem->symlink($filename, $linknameA);
+ $this->filesystem->symlink($linknameA, $linknameB);
+
+ $this->filesystem->appendToFile($linknameA, 'bar');
+ $this->filesystem->appendToFile($linknameB, 'baz');
+
+ $this->assertFileExists($filename);
+ $this->assertFileExists($linknameA);
+ $this->assertFileExists($linknameB);
+ $this->assertStringEqualsFile($filename, 'foobarbaz');
+ $this->assertStringEqualsFile($linknameA, 'foobarbaz');
+ $this->assertStringEqualsFile($linknameB, 'foobarbaz');
+
+ $this->filesystem->remove($filename);
+ $this->filesystem->appendToFile($linknameB, 'foo');
+ $this->filesystem->appendToFile($linknameA, 'bar');
+
+ $this->assertFileExists($filename);
+ $this->assertStringEqualsFile($filename, 'foobar');
+ $this->assertStringEqualsFile($linknameA, 'foobar');
+ $this->assertStringEqualsFile($linknameB, 'foobar');
+ }
+
public function testAppendToFileWithScheme()
{
$scheme = 'file://';
diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
index 7b8d5b521d123..3b0f89509f65c 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
@@ -71,7 +71,10 @@ public function getArguments(Request $request, callable $controller, \Reflection
throw new ResolverNotFoundException($resolverName, $this->namedResolvers instanceof ServiceProviderInterface ? array_keys($this->namedResolvers->getProvidedServices()) : []);
}
- $argumentValueResolvers = [$this->namedResolvers->get($resolverName)];
+ $argumentValueResolvers = [
+ $this->namedResolvers->get($resolverName),
+ new DefaultValueResolver(),
+ ];
}
}
diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/QueryParameterValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/QueryParameterValueResolver.php
index 7255a792ba754..f2e4bee812d79 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/QueryParameterValueResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/QueryParameterValueResolver.php
@@ -31,12 +31,8 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
$name = $attribute->name ?? $argument->getName();
if (!$request->query->has($name)) {
- if ($argument->hasDefaultValue()) {
- return [$argument->getDefaultValue()];
- }
-
- if ($argument->isNullable()) {
- return [null];
+ if ($argument->isNullable() || $argument->hasDefaultValue()) {
+ return [];
}
throw new NotFoundHttpException(sprintf('Missing query parameter "%s".', $name));
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 0bd152bc8208a..c98705163d086 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static array $freshCache = [];
- public const VERSION = '6.3.0-RC2';
+ public const VERSION = '6.3.0';
public const VERSION_ID = 60300;
public const MAJOR_VERSION = 6;
public const MINOR_VERSION = 3;
public const RELEASE_VERSION = 0;
- public const EXTRA_VERSION = 'RC2';
+ public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '01/2024';
public const END_OF_LIFE = '01/2024';
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/QueryParameterValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/QueryParameterValueResolverTest.php
index 1383475988b52..539aaac78ee65 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/QueryParameterValueResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/QueryParameterValueResolverTest.php
@@ -179,14 +179,14 @@ public static function provideTestResolve(): iterable
yield 'parameter not found but nullable' => [
Request::create('/', 'GET'),
new ArgumentMetadata('firstName', 'string', false, false, false, true, [new MapQueryParameter()]),
- [null],
+ [],
null,
];
yield 'parameter not found but optional' => [
Request::create('/', 'GET'),
new ArgumentMetadata('firstName', 'string', false, true, false, attributes: [new MapQueryParameter()]),
- [false],
+ [],
null,
];
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php
index e62001633ca93..ef44f45bae078 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolverTest.php
@@ -296,6 +296,26 @@ public function testTargetedResolver()
$this->assertSame([1], $resolver->getArguments($request, $controller));
}
+ public function testTargetedResolverWithDefaultValue()
+ {
+ $resolver = self::getResolver([], [RequestAttributeValueResolver::class => new RequestAttributeValueResolver()]);
+
+ $request = Request::create('/');
+ $controller = $this->controllerTargetingResolverWithDefaultValue(...);
+
+ $this->assertSame([2], $resolver->getArguments($request, $controller));
+ }
+
+ public function testTargetedResolverWithNullableValue()
+ {
+ $resolver = self::getResolver([], [RequestAttributeValueResolver::class => new RequestAttributeValueResolver()]);
+
+ $request = Request::create('/');
+ $controller = $this->controllerTargetingResolverWithNullableValue(...);
+
+ $this->assertSame([null], $resolver->getArguments($request, $controller));
+ }
+
public function testDisabledResolver()
{
$resolver = self::getResolver(namedResolvers: []);
@@ -373,6 +393,14 @@ public function controllerTargetingResolver(#[ValueResolver(DefaultValueResolver
{
}
+ public function controllerTargetingResolverWithDefaultValue(#[ValueResolver(RequestAttributeValueResolver::class)] int $foo = 2)
+ {
+ }
+
+ public function controllerTargetingResolverWithNullableValue(#[ValueResolver(RequestAttributeValueResolver::class)] ?int $foo)
+ {
+ }
+
public function controllerDisablingResolver(#[ValueResolver(RequestAttributeValueResolver::class, disabled: true)] int $foo = 1)
{
}
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
index 2426730798689..0f7620763f950 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
@@ -145,7 +145,7 @@ public function initialize(): void
if ($this->streamContextOptions) {
$options = array_merge($options, $this->streamContextOptions);
}
- // do it unconditionnally as it will be used by STARTTLS as well if supported
+ // do it unconditionally as it will be used by STARTTLS as well if supported
$options['ssl']['crypto_method'] ??= \STREAM_CRYPTO_METHOD_TLS_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
$streamContext = stream_context_create($options);
diff --git a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneOptions.php b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneOptions.php
index 122cb93680f48..ca264d8018eae 100644
--- a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneOptions.php
+++ b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneOptions.php
@@ -15,6 +15,8 @@
/**
* @author gnito-org
+ *
+ * @see https://ceo-be.multimediabs.com/attachments/hosted/lightApiManualsFR
*/
final class ContactEveryoneOptions implements MessageOptionsInterface
{
@@ -35,7 +37,7 @@ public function getRecipientId(): ?string
*/
public function diffusionName(string $diffusionName): static
{
- $this->options['diffusion_name'] = $diffusionName;
+ $this->options['diffusionname'] = $diffusionName;
return $this;
}
@@ -50,6 +52,28 @@ public function category(string $category): static
return $this;
}
+ /**
+ * @param 'fr_FR'|'en_GB' $locale
+ *
+ * @return $this
+ */
+ public function locale(string $locale): static
+ {
+ $this->options['locale'] = $locale;
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function unicode(bool $unicode): static
+ {
+ $this->options['xcharset'] = $unicode ? 'true' : 'false';
+
+ return $this;
+ }
+
public function toArray(): array
{
return $this->options;
diff --git a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneTransport.php b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneTransport.php
index 0f09f1e54cde6..f9d2e6c2dfe66 100644
--- a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneTransport.php
+++ b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/ContactEveryoneTransport.php
@@ -73,7 +73,9 @@ protected function doSend(MessageInterface $message): SentMessage
}
$options = $message->getOptions()?->toArray() ?? [];
- $options['xcharset'] = 'true';
+ $options['category'] ??= $this->category;
+ $options['diffusionname'] ??= $this->diffusionName;
+ $options['xcharset'] ??= 'true';
$options['token'] = $this->token;
$options['to'] = $message->getPhone();
$options['msg'] = $message->getSubject();
diff --git a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/Tests/ContactEveryoneOptionsTest.php b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/Tests/ContactEveryoneOptionsTest.php
index 89b367509bd88..4485d18e5b3b3 100644
--- a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/Tests/ContactEveryoneOptionsTest.php
+++ b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/Tests/ContactEveryoneOptionsTest.php
@@ -24,7 +24,7 @@ public function testContactEveryoneOptions()
self::assertSame([
'category' => 'test_category',
- 'diffusion_name' => 'test_diffusion_name',
+ 'diffusionname' => 'test_diffusion_name',
], $contactEveryoneOptions->toArray());
}
}
diff --git a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php
index 047cc3318e017..0623a60016a0d 100644
--- a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php
+++ b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php
@@ -20,8 +20,9 @@
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
+use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;
-use Symfony\Component\Clock\NativeClock;
+use Symfony\Component\Clock\Clock;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\AccessToken\Oidc\Exception\InvalidSignatureException;
@@ -40,9 +41,11 @@ final class OidcTokenHandler implements AccessTokenHandlerInterface
public function __construct(
private Algorithm $signatureAlgorithm,
private JWK $jwk,
- private ?LoggerInterface $logger = null,
+ private string $audience,
+ private array $issuers,
private string $claim = 'sub',
- private ?string $audience = null
+ private ?LoggerInterface $logger = null,
+ private ClockInterface $clock = new Clock()
) {
}
@@ -74,15 +77,13 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
$headerCheckerManager->check($jws, 0);
// Verify the claims
- $clock = class_exists(NativeClock::class) ? new NativeClock() : null;
$checkers = [
- new Checker\IssuedAtChecker(0, false, $clock),
- new Checker\NotBeforeChecker(0, false, $clock),
- new Checker\ExpirationTimeChecker(0, false, $clock),
+ new Checker\IssuedAtChecker(0, false, $this->clock),
+ new Checker\NotBeforeChecker(0, false, $this->clock),
+ new Checker\ExpirationTimeChecker(0, false, $this->clock),
+ new Checker\AudienceChecker($this->audience),
+ new Checker\IssuerChecker($this->issuers),
];
- if ($this->audience) {
- $checkers[] = new Checker\AudienceChecker($this->audience);
- }
$claimCheckerManager = new ClaimCheckerManager($checkers);
// if this check fails, an InvalidClaimException is thrown
$claimCheckerManager->check($claims);
@@ -93,7 +94,7 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
// UserLoader argument can be overridden by a UserProvider on AccessTokenAuthenticator::authenticate
return new UserBadge($claims[$this->claim], fn () => $this->createUser($claims), $claims);
- } catch (\Throwable $e) {
+ } catch (\Exception $e) {
$this->logger?->error('An error while decoding and validating the token.', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
diff --git a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php
index d69775f192e20..26279ebf19e68 100644
--- a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php
+++ b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php
@@ -49,7 +49,7 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
// UserLoader argument can be overridden by a UserProvider on AccessTokenAuthenticator::authenticate
return new UserBadge($claims[$this->claim], fn () => $this->createUser($claims), $claims);
- } catch (\Throwable $e) {
+ } catch (\Exception $e) {
$this->logger?->error('An error occurred on OIDC server.', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
diff --git a/src/Symfony/Component/Security/Http/Tests/AccessToken/Oidc/OidcTokenHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/AccessToken/Oidc/OidcTokenHandlerTest.php
index d9fa14f0b8ff5..8c8d86284b59a 100644
--- a/src/Symfony/Component/Security/Http/Tests/AccessToken/Oidc/OidcTokenHandlerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/AccessToken/Oidc/OidcTokenHandlerTest.php
@@ -40,7 +40,7 @@ public function testGetsUserIdentifierFromSignedToken(string $claim, string $exp
'iat' => $time,
'nbf' => $time,
'exp' => $time + 3600,
- 'iss' => 'https://www.example.com/',
+ 'iss' => 'https://www.example.com',
'aud' => self::AUDIENCE,
'sub' => 'e21bf182-1538-406e-8ccb-e25a17aba39f',
'email' => 'foo@example.com',
@@ -54,9 +54,10 @@ public function testGetsUserIdentifierFromSignedToken(string $claim, string $exp
$userBadge = (new OidcTokenHandler(
new ES256(),
$this->getJWK(),
- $loggerMock,
+ self::AUDIENCE,
+ ['https://www.example.com'],
$claim,
- self::AUDIENCE
+ $loggerMock,
))->getUserBadgeFrom($token);
$actualUser = $userBadge->getUserLoader()();
@@ -87,9 +88,10 @@ public function testThrowsAnErrorIfTokenIsInvalid(string $token)
(new OidcTokenHandler(
new ES256(),
$this->getJWK(),
- $loggerMock,
+ self::AUDIENCE,
+ ['https://www.example.com'],
'sub',
- self::AUDIENCE
+ $loggerMock,
))->getUserBadgeFrom($token);
}
@@ -103,7 +105,7 @@ public static function getInvalidTokens(): iterable
'iat' => time() - 3600,
'nbf' => time() - 3600,
'exp' => time() - 3590,
- 'iss' => 'https://www.example.com/',
+ 'iss' => 'https://www.example.com',
'aud' => self::AUDIENCE,
'sub' => 'e21bf182-1538-406e-8ccb-e25a17aba39f',
'email' => 'foo@example.com',
@@ -115,7 +117,7 @@ public static function getInvalidTokens(): iterable
'iat' => time(),
'nbf' => time(),
'exp' => time() + 3590,
- 'iss' => 'https://www.example.com/',
+ 'iss' => 'https://www.example.com',
'aud' => 'invalid',
'sub' => 'e21bf182-1538-406e-8ccb-e25a17aba39f',
'email' => 'foo@example.com',
@@ -136,7 +138,7 @@ public function testThrowsAnErrorIfUserPropertyIsMissing()
'iat' => $time,
'nbf' => $time,
'exp' => $time + 3600,
- 'iss' => 'https://www.example.com/',
+ 'iss' => 'https://www.example.com',
'aud' => self::AUDIENCE,
'sub' => 'e21bf182-1538-406e-8ccb-e25a17aba39f',
];
@@ -145,9 +147,10 @@ public function testThrowsAnErrorIfUserPropertyIsMissing()
(new OidcTokenHandler(
new ES256(),
self::getJWK(),
- $loggerMock,
+ self::AUDIENCE,
+ ['https://www.example.com'],
'email',
- self::AUDIENCE
+ $loggerMock,
))->getUserBadgeFrom($token);
}
diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json
index b3c688147e9ea..74f7ddb8f50d0 100644
--- a/src/Symfony/Component/Security/Http/composer.json
+++ b/src/Symfony/Component/Security/Http/composer.json
@@ -26,6 +26,7 @@
},
"require-dev": {
"symfony/cache": "^5.4|^6.0",
+ "symfony/clock": "^6.3",
"symfony/expression-language": "^5.4|^6.0",
"symfony/http-client-contracts": "^3.0",
"symfony/rate-limiter": "^5.4|^6.0",
@@ -37,6 +38,7 @@
"web-token/jwt-signature-algorithm-ecdsa": "^3.1"
},
"conflict": {
+ "symfony/clock": "<6.3",
"symfony/event-dispatcher": "<5.4.9|>=6,<6.0.9",
"symfony/http-client-contracts": "<3.0",
"symfony/security-bundle": "<5.4",
diff --git a/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php b/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php
index 8bff9de933413..63efb71d7005b 100644
--- a/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php
+++ b/src/Symfony/Component/Serializer/Context/Encoder/YamlEncoderContextBuilder.php
@@ -59,7 +59,7 @@ public function withFlags(?int $flags): static
}
/**
- * Configures whether to perserve empty objects "{}" or to convert them to null.
+ * Configures whether to preserve empty objects "{}" or to convert them to null.
*/
public function withPreservedEmptyObjects(?bool $preserveEmptyObjects): static
{
diff --git a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php
index e4168cc14bf09..674b78b3eae5b 100644
--- a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php
+++ b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php
@@ -255,13 +255,13 @@ public static function getChooseTests()
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 5],
- // with double-quotes and id split accros lines
+ // with double-quotes and id split across lines
['This is a text with a
new-line in it. Selector = 1.', '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
new-line in it. Selector = 1.|[1,Inf]This is a text with a
new-line in it. Selector > 1.', 1],
- // with single-quotes and id split accros lines
+ // with single-quotes and id split across lines
['This is a text with a
new-line in it. Selector > 1.', '{0}This is a text with a
new-line in it. Selector = 0.|{1}This is a text with a
@@ -269,7 +269,7 @@ public static function getChooseTests()
new-line in it. Selector > 1.', 5],
// with single-quotes and \n in text
['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
- // with double-quotes and id split accros lines
+ // with double-quotes and id split across lines
["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
// escape pipe
['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],