diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md
index 2fed4defb0..d3de7b168c 100644
--- a/UPGRADE-4.4.md
+++ b/UPGRADE-4.4.md
@@ -1,11 +1,6 @@
UPGRADE FROM 4.3 to 4.4
=======================
-HttpKernel
-----------
-
- * The `DebugHandlersListener` class has been marked as `final`
-
DependencyInjection
-------------------
@@ -46,18 +41,24 @@ DependencyInjection
arguments: [!tagged_iterator app.handler]
```
+FrameworkBundle
+---------------
+
+ * Deprecated support for `templating` engine in `TemplateController`, use Twig instead
+ * The `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()`
+ has been deprecated.
+ * The `ControllerResolver` and `DelegatingLoader` classes have been marked as `final`.
+ * The `controller_name_converter` and `resolve_controller_name_subscriber` services have been deprecated.
+
HttpClient
----------
* Added method `cancel()` to `ResponseInterface`
-FrameworkBundle
----------------
+HttpKernel
+----------
- * The `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()`
- has been deprecated.
- * The `ControllerResolver` and `DelegatingLoader` classes have been marked as `final`.
- * The `controller_name_converter` and `resolve_controller_name_subscriber` services have been deprecated.
+ * The `DebugHandlersListener` class has been marked as `final`
Messenger
---------
@@ -65,11 +66,6 @@ Messenger
* Deprecated passing a `ContainerInterface` instance as first argument of the `ConsumeMessagesCommand` constructor,
pass a `RoutableMessageBus` instance instead.
-FrameworkBundle
----------------
-
- * Deprecated support for `templating` engine in `TemplateController`, use Twig instead
-
MonologBridge
--------------
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
index 36d80501bd..17598fa958 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
@@ -71,6 +71,7 @@
+
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
index 228db30a73..7e8cb99356 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
@@ -141,15 +141,6 @@ class MainConfiguration implements ConfigurationInterface
->integerNode('port')->defaultNull()->end()
->arrayNode('ips')
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
- ->beforeNormalization()->always()->then(function ($v) {
- foreach ($v as $ip) {
- if (false === $this->isValidIp($ip)) {
- throw new \LogicException(sprintf('The given "%s" value in the "access_control" config option is not a valid IP address.', $ip));
- }
- }
-
- return $v;
- })->end()
->prototype('scalar')->end()
->end()
->arrayNode('methods')
@@ -401,30 +392,4 @@ class MainConfiguration implements ConfigurationInterface
->end()
;
}
-
- private function isValidIp(string $cidr): bool
- {
- $cidrParts = explode('/', $cidr);
-
- if (1 === \count($cidrParts)) {
- return false !== filter_var($cidrParts[0], FILTER_VALIDATE_IP);
- }
-
- $ip = $cidrParts[0];
- $netmask = $cidrParts[1];
-
- if (!ctype_digit($netmask)) {
- return false;
- }
-
- if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
- return $netmask <= 32;
- }
-
- if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
- return $netmask <= 128;
- }
-
- return false;
- }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index a05e2f6367..635104f17a 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -694,20 +694,32 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
return $this->expressions[$id] = new Reference($id);
}
- private function createRequestMatcher($container, $path = null, $host = null, int $port = null, $methods = [], $ip = null, array $attributes = [])
+ private function createRequestMatcher(ContainerBuilder $container, $path = null, $host = null, int $port = null, $methods = [], array $ips = null, array $attributes = [])
{
if ($methods) {
$methods = array_map('strtoupper', (array) $methods);
}
- $id = '.security.request_matcher.'.ContainerBuilder::hash([$path, $host, $port, $methods, $ip, $attributes]);
+ if (null !== $ips) {
+ foreach ($ips as $ip) {
+ $container->resolveEnvPlaceholders($ip, null, $usedEnvs);
+
+ if (!$usedEnvs && !$this->isValidIp($ip)) {
+ throw new \LogicException(sprintf('The given value "%s" in the "security.access_control" config option is not a valid IP address.', $ip));
+ }
+
+ $usedEnvs = null;
+ }
+ }
+
+ $id = '.security.request_matcher.'.ContainerBuilder::hash([$path, $host, $port, $methods, $ips, $attributes]);
if (isset($this->requestMatchers[$id])) {
return $this->requestMatchers[$id];
}
// only add arguments that are necessary
- $arguments = [$path, $host, $methods, $ip, $attributes, null, $port];
+ $arguments = [$path, $host, $methods, $ips, $attributes, null, $port];
while (\count($arguments) > 0 && !end($arguments)) {
array_pop($arguments);
}
@@ -751,4 +763,30 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
// first assemble the factories
return new MainConfiguration($this->factories, $this->userProviderFactories);
}
+
+ private function isValidIp(string $cidr): bool
+ {
+ $cidrParts = explode('/', $cidr);
+
+ if (1 === \count($cidrParts)) {
+ return false !== filter_var($cidrParts[0], FILTER_VALIDATE_IP);
+ }
+
+ $ip = $cidrParts[0];
+ $netmask = $cidrParts[1];
+
+ if (!ctype_digit($netmask)) {
+ return false;
+ }
+
+ if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ return $netmask <= 32;
+ }
+
+ if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return $netmask <= 128;
+ }
+
+ return false;
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml
index 2fff93dcad..bf82a203d8 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml
@@ -40,6 +40,12 @@ secured-by-one-real-ip-with-mask:
secured-by-one-real-ipv6:
path: /secured-by-one-real-ipv6
+secured-by-one-env-placeholder:
+ path: /secured-by-one-env-placeholder
+
+secured-by-one-env-placeholder-and-one-real-ip:
+ path: /secured-by-one-env-placeholder-and-one-real-ip
+
form_logout:
path: /logout_path
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
index 682c561ac5..c38bc1a4bf 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php
@@ -109,7 +109,7 @@ class SecurityRoutingIntegrationTest extends WebTestCase
public function testInvalidIpsInAccessControl()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('The given "256.357.458.559" value in the "access_control" config option is not a valid IP address.');
+ $this->expectExceptionMessage('The given value "256.357.458.559" in the "security.access_control" config option is not a valid IP address.');
$client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'invalid_ip_access_control.yml']);
$client->request('GET', '/unprotected_resource');
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
index df03c20c5c..4e2ac1e11b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml
@@ -1,6 +1,9 @@
imports:
- { resource: ./../config/default.yml }
+parameters:
+ env(APP_IP): '127.0.0.1'
+
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
@@ -43,6 +46,8 @@ security:
- { path: ^/secured-by-one-real-ip$, ips: 198.51.100.0, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured-by-one-real-ip-with-mask$, ips: '203.0.113.0/24', roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured-by-one-real-ipv6$, ips: 0:0:0:0:0:ffff:c633:6400, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/secured-by-one-env-placeholder$, ips: '%env(APP_IP)%', roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/secured-by-one-env-placeholder-and-one-real-ip$, ips: ['%env(APP_IP)%', 198.51.100.0], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/highly_protected_resource$, roles: IS_ADMIN }
- { path: ^/protected-via-expression$, allow_if: "(is_anonymous() and request.headers.get('user-agent') matches '/Firefox/i') or is_granted('ROLE_USER')" }
- { path: .*, roles: IS_AUTHENTICATED_FULLY }
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml
index e4f46c4704..cc6503affb 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml
@@ -19,4 +19,4 @@ security:
access_control:
# the '256.357.458.559' IP is wrong on purpose, to check invalid IP errors
- - { path: ^/unprotected_resource$, ips: [1.1.1.1, 256.357.458.559], roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/unprotected_resource$, ips: [1.1.1.1, '%env(APP_IP)%', 256.357.458.559], roles: IS_AUTHENTICATED_ANONYMOUSLY }
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php
index 4dc71db4f2..794c42270a 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php
@@ -159,7 +159,7 @@ class FileType extends AbstractType
}
if (null !== $this->translator) {
- $message = $this->translator->trans($messageTemplate, $messageParameters);
+ $message = $this->translator->trans($messageTemplate, $messageParameters, 'validators');
} else {
$message = strtr($messageTemplate, $messageParameters);
}
diff --git a/src/Symfony/Component/HttpFoundation/JsonResponse.php b/src/Symfony/Component/HttpFoundation/JsonResponse.php
index 82d182a77a..c0a6916f05 100644
--- a/src/Symfony/Component/HttpFoundation/JsonResponse.php
+++ b/src/Symfony/Component/HttpFoundation/JsonResponse.php
@@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
* object. It is however recommended that you do return an object as it
* protects yourself against XSSI and JSON-JavaScript Hijacking.
*
- * @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside
+ * @see https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/AJAX_Security_Cheat_Sheet.md#always-return-json-with-an-object-on-the-outside
*
* @author Igor Wiedler
*/
diff --git a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php
index 6f6b8b22a6..ecd72a7be9 100644
--- a/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php
+++ b/src/Symfony/Component/Messenger/Transport/AmqpExt/Connection.php
@@ -430,6 +430,7 @@ class Connection
$this->amqpChannel = null;
$this->amqpQueues = [];
$this->amqpExchange = null;
+ $this->amqpDelayExchange = null;
}
private function shouldSetup(): bool
diff --git a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php
index fceb3e7c21..85de9509e9 100644
--- a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php
+++ b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php
@@ -26,7 +26,7 @@ class AuthenticationException extends RuntimeException
/**
* Get the token.
*
- * @return TokenInterface
+ * @return TokenInterface|null
*/
public function getToken()
{
diff --git a/src/Symfony/Component/Validator/Constraints/GroupSequence.php b/src/Symfony/Component/Validator/Constraints/GroupSequence.php
index a39d712bf4..fc34ca1a45 100644
--- a/src/Symfony/Component/Validator/Constraints/GroupSequence.php
+++ b/src/Symfony/Component/Validator/Constraints/GroupSequence.php
@@ -57,7 +57,7 @@ class GroupSequence
/**
* The groups in the sequence.
*
- * @var string[]|array[]|GroupSequence[]
+ * @var string[]|string[][]|GroupSequence[]
*/
public $groups;
diff --git a/src/Symfony/Component/Validator/GroupSequenceProviderInterface.php b/src/Symfony/Component/Validator/GroupSequenceProviderInterface.php
index 5894397da4..1ce504331f 100644
--- a/src/Symfony/Component/Validator/GroupSequenceProviderInterface.php
+++ b/src/Symfony/Component/Validator/GroupSequenceProviderInterface.php
@@ -22,7 +22,7 @@ interface GroupSequenceProviderInterface
* Returns which validation groups should be used for a certain state
* of the object.
*
- * @return string[]|GroupSequence An array of validation groups
+ * @return string[]|string[][]|GroupSequence An array of validation groups
*/
public function getGroupSequence();
}