Merge branch '4.4'
* 4.4: fix order of items in upgrade file fix translation domain tag the FileType service as a form type don't validate IP addresses from env var placeholders [Validator] Fix GroupSequenceProvider annotation [Messenger] fix delay exchange recreation after disconnect Update ajax security cheat sheet link Fix AuthenticationException::getToken typehint
This commit is contained in:
commit
1ac9cc285c
@ -1,11 +1,6 @@
|
|||||||
UPGRADE FROM 4.3 to 4.4
|
UPGRADE FROM 4.3 to 4.4
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
HttpKernel
|
|
||||||
----------
|
|
||||||
|
|
||||||
* The `DebugHandlersListener` class has been marked as `final`
|
|
||||||
|
|
||||||
DependencyInjection
|
DependencyInjection
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
@ -46,18 +41,24 @@ DependencyInjection
|
|||||||
arguments: [!tagged_iterator app.handler]
|
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
|
HttpClient
|
||||||
----------
|
----------
|
||||||
|
|
||||||
* Added method `cancel()` to `ResponseInterface`
|
* Added method `cancel()` to `ResponseInterface`
|
||||||
|
|
||||||
FrameworkBundle
|
HttpKernel
|
||||||
---------------
|
----------
|
||||||
|
|
||||||
* The `$parser` argument of `ControllerResolver::__construct()` and `DelegatingLoader::__construct()`
|
* The `DebugHandlersListener` class has been marked as `final`
|
||||||
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.
|
|
||||||
|
|
||||||
Messenger
|
Messenger
|
||||||
---------
|
---------
|
||||||
@ -65,11 +66,6 @@ Messenger
|
|||||||
* Deprecated passing a `ContainerInterface` instance as first argument of the `ConsumeMessagesCommand` constructor,
|
* Deprecated passing a `ContainerInterface` instance as first argument of the `ConsumeMessagesCommand` constructor,
|
||||||
pass a `RoutableMessageBus` instance instead.
|
pass a `RoutableMessageBus` instance instead.
|
||||||
|
|
||||||
FrameworkBundle
|
|
||||||
---------------
|
|
||||||
|
|
||||||
* Deprecated support for `templating` engine in `TemplateController`, use Twig instead
|
|
||||||
|
|
||||||
MonologBridge
|
MonologBridge
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
<argument type="service" id="form.choice_list_factory"/>
|
<argument type="service" id="form.choice_list_factory"/>
|
||||||
</service>
|
</service>
|
||||||
<service id="form.type.file" class="Symfony\Component\Form\Extension\Core\Type\FileType" public="true">
|
<service id="form.type.file" class="Symfony\Component\Form\Extension\Core\Type\FileType" public="true">
|
||||||
|
<tag name="form.type" />
|
||||||
<argument type="service" id="translator" on-invalid="ignore" />
|
<argument type="service" id="translator" on-invalid="ignore" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
@ -141,15 +141,6 @@ class MainConfiguration implements ConfigurationInterface
|
|||||||
->integerNode('port')->defaultNull()->end()
|
->integerNode('port')->defaultNull()->end()
|
||||||
->arrayNode('ips')
|
->arrayNode('ips')
|
||||||
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
|
->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()
|
->prototype('scalar')->end()
|
||||||
->end()
|
->end()
|
||||||
->arrayNode('methods')
|
->arrayNode('methods')
|
||||||
@ -401,30 +392,4 @@ class MainConfiguration implements ConfigurationInterface
|
|||||||
->end()
|
->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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -694,20 +694,32 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
return $this->expressions[$id] = new Reference($id);
|
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) {
|
if ($methods) {
|
||||||
$methods = array_map('strtoupper', (array) $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])) {
|
if (isset($this->requestMatchers[$id])) {
|
||||||
return $this->requestMatchers[$id];
|
return $this->requestMatchers[$id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// only add arguments that are necessary
|
// 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)) {
|
while (\count($arguments) > 0 && !end($arguments)) {
|
||||||
array_pop($arguments);
|
array_pop($arguments);
|
||||||
}
|
}
|
||||||
@ -751,4 +763,30 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
// first assemble the factories
|
// first assemble the factories
|
||||||
return new MainConfiguration($this->factories, $this->userProviderFactories);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,12 @@ secured-by-one-real-ip-with-mask:
|
|||||||
secured-by-one-real-ipv6:
|
secured-by-one-real-ipv6:
|
||||||
path: /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:
|
form_logout:
|
||||||
path: /logout_path
|
path: /logout_path
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class SecurityRoutingIntegrationTest extends WebTestCase
|
|||||||
public function testInvalidIpsInAccessControl()
|
public function testInvalidIpsInAccessControl()
|
||||||
{
|
{
|
||||||
$this->expectException(\LogicException::class);
|
$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 = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'invalid_ip_access_control.yml']);
|
||||||
$client->request('GET', '/unprotected_resource');
|
$client->request('GET', '/unprotected_resource');
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
imports:
|
imports:
|
||||||
- { resource: ./../config/default.yml }
|
- { resource: ./../config/default.yml }
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
env(APP_IP): '127.0.0.1'
|
||||||
|
|
||||||
security:
|
security:
|
||||||
encoders:
|
encoders:
|
||||||
Symfony\Component\Security\Core\User\User: plaintext
|
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$, 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-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-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: ^/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: ^/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 }
|
- { path: .*, roles: IS_AUTHENTICATED_FULLY }
|
||||||
|
@ -19,4 +19,4 @@ security:
|
|||||||
|
|
||||||
access_control:
|
access_control:
|
||||||
# the '256.357.458.559' IP is wrong on purpose, to check invalid IP errors
|
# 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 }
|
||||||
|
@ -159,7 +159,7 @@ class FileType extends AbstractType
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $this->translator) {
|
if (null !== $this->translator) {
|
||||||
$message = $this->translator->trans($messageTemplate, $messageParameters);
|
$message = $this->translator->trans($messageTemplate, $messageParameters, 'validators');
|
||||||
} else {
|
} else {
|
||||||
$message = strtr($messageTemplate, $messageParameters);
|
$message = strtr($messageTemplate, $messageParameters);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
|
|||||||
* object. It is however recommended that you do return an object as it
|
* object. It is however recommended that you do return an object as it
|
||||||
* protects yourself against XSSI and JSON-JavaScript Hijacking.
|
* 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 <igor@wiedler.ch>
|
* @author Igor Wiedler <igor@wiedler.ch>
|
||||||
*/
|
*/
|
||||||
|
@ -430,6 +430,7 @@ class Connection
|
|||||||
$this->amqpChannel = null;
|
$this->amqpChannel = null;
|
||||||
$this->amqpQueues = [];
|
$this->amqpQueues = [];
|
||||||
$this->amqpExchange = null;
|
$this->amqpExchange = null;
|
||||||
|
$this->amqpDelayExchange = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function shouldSetup(): bool
|
private function shouldSetup(): bool
|
||||||
|
@ -26,7 +26,7 @@ class AuthenticationException extends RuntimeException
|
|||||||
/**
|
/**
|
||||||
* Get the token.
|
* Get the token.
|
||||||
*
|
*
|
||||||
* @return TokenInterface
|
* @return TokenInterface|null
|
||||||
*/
|
*/
|
||||||
public function getToken()
|
public function getToken()
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ class GroupSequence
|
|||||||
/**
|
/**
|
||||||
* The groups in the sequence.
|
* The groups in the sequence.
|
||||||
*
|
*
|
||||||
* @var string[]|array[]|GroupSequence[]
|
* @var string[]|string[][]|GroupSequence[]
|
||||||
*/
|
*/
|
||||||
public $groups;
|
public $groups;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ interface GroupSequenceProviderInterface
|
|||||||
* Returns which validation groups should be used for a certain state
|
* Returns which validation groups should be used for a certain state
|
||||||
* of the object.
|
* of the object.
|
||||||
*
|
*
|
||||||
* @return string[]|GroupSequence An array of validation groups
|
* @return string[]|string[][]|GroupSequence An array of validation groups
|
||||||
*/
|
*/
|
||||||
public function getGroupSequence();
|
public function getGroupSequence();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user