[Form] Skip CSRF validation on form when POST max size is exceeded
This commit is contained in:
parent
45907598bc
commit
289531f0d0
@ -16,6 +16,7 @@
|
|||||||
<argument>%form.type_extension.csrf.field_name%</argument>
|
<argument>%form.type_extension.csrf.field_name%</argument>
|
||||||
<argument type="service" id="translator.default" />
|
<argument type="service" id="translator.default" />
|
||||||
<argument>%validator.translation_domain%</argument>
|
<argument>%validator.translation_domain%</argument>
|
||||||
|
<argument type="service" id="form.server_params" />
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
|
|||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\Form\FormError;
|
use Symfony\Component\Form\FormError;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
|
use Symfony\Component\Form\Util\ServerParams;
|
||||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
@ -68,6 +69,11 @@ class CsrfValidationListener implements EventSubscriberInterface
|
|||||||
*/
|
*/
|
||||||
private $translationDomain;
|
private $translationDomain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ServerParams
|
||||||
|
*/
|
||||||
|
private $serverParams;
|
||||||
|
|
||||||
public static function getSubscribedEvents()
|
public static function getSubscribedEvents()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
@ -75,7 +81,7 @@ class CsrfValidationListener implements EventSubscriberInterface
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct($fieldName, $tokenManager, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null)
|
public function __construct($fieldName, $tokenManager, $tokenId, $errorMessage, TranslatorInterface $translator = null, $translationDomain = null, ServerParams $serverParams = null)
|
||||||
{
|
{
|
||||||
if ($tokenManager instanceof CsrfProviderInterface) {
|
if ($tokenManager instanceof CsrfProviderInterface) {
|
||||||
$tokenManager = new CsrfProviderAdapter($tokenManager);
|
$tokenManager = new CsrfProviderAdapter($tokenManager);
|
||||||
@ -89,13 +95,15 @@ class CsrfValidationListener implements EventSubscriberInterface
|
|||||||
$this->errorMessage = $errorMessage;
|
$this->errorMessage = $errorMessage;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->translationDomain = $translationDomain;
|
$this->translationDomain = $translationDomain;
|
||||||
|
$this->serverParams = $serverParams ?: new ServerParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function preSubmit(FormEvent $event)
|
public function preSubmit(FormEvent $event)
|
||||||
{
|
{
|
||||||
$form = $event->getForm();
|
$form = $event->getForm();
|
||||||
|
$postRequestSizeExceeded = $form->getConfig()->getMethod() === 'POST' && $this->serverParams->hasPostMaxSizeBeenExceeded();
|
||||||
|
|
||||||
if ($form->isRoot() && $form->getConfig()->getOption('compound')) {
|
if ($form->isRoot() && $form->getConfig()->getOption('compound') && !$postRequestSizeExceeded) {
|
||||||
$data = $event->getData();
|
$data = $event->getData();
|
||||||
|
|
||||||
if (!isset($data[$this->fieldName]) || !$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))) {
|
if (!isset($data[$this->fieldName]) || !$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))) {
|
||||||
|
@ -20,6 +20,7 @@ use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
|
|||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormView;
|
use Symfony\Component\Form\FormView;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\Util\ServerParams;
|
||||||
use Symfony\Component\OptionsResolver\Options;
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||||
@ -55,7 +56,12 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
|
|||||||
*/
|
*/
|
||||||
private $translationDomain;
|
private $translationDomain;
|
||||||
|
|
||||||
public function __construct($defaultTokenManager, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null)
|
/**
|
||||||
|
* @var ServerParams
|
||||||
|
*/
|
||||||
|
private $serverParams;
|
||||||
|
|
||||||
|
public function __construct($defaultTokenManager, $defaultEnabled = true, $defaultFieldName = '_token', TranslatorInterface $translator = null, $translationDomain = null, ServerParams $serverParams = null)
|
||||||
{
|
{
|
||||||
if ($defaultTokenManager instanceof CsrfProviderInterface) {
|
if ($defaultTokenManager instanceof CsrfProviderInterface) {
|
||||||
$defaultTokenManager = new CsrfProviderAdapter($defaultTokenManager);
|
$defaultTokenManager = new CsrfProviderAdapter($defaultTokenManager);
|
||||||
@ -68,6 +74,7 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
|
|||||||
$this->defaultFieldName = $defaultFieldName;
|
$this->defaultFieldName = $defaultFieldName;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
$this->translationDomain = $translationDomain;
|
$this->translationDomain = $translationDomain;
|
||||||
|
$this->serverParams = $serverParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +96,8 @@ class FormTypeCsrfExtension extends AbstractTypeExtension
|
|||||||
$options['csrf_token_id'] ?: ($builder->getName() ?: get_class($builder->getType()->getInnerType())),
|
$options['csrf_token_id'] ?: ($builder->getName() ?: get_class($builder->getType()->getInnerType())),
|
||||||
$options['csrf_message'],
|
$options['csrf_message'],
|
||||||
$this->translator,
|
$this->translator,
|
||||||
$this->translationDomain
|
$this->translationDomain,
|
||||||
|
$this->serverParams
|
||||||
))
|
))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@ -73,10 +73,7 @@ class HttpFoundationRequestHandler implements RequestHandlerInterface
|
|||||||
// Mark the form with an error if the uploaded size was too large
|
// Mark the form with an error if the uploaded size was too large
|
||||||
// This is done here and not in FormValidator because $_POST is
|
// This is done here and not in FormValidator because $_POST is
|
||||||
// empty when that error occurs. Hence the form is never submitted.
|
// empty when that error occurs. Hence the form is never submitted.
|
||||||
$contentLength = $this->serverParams->getContentLength();
|
if ($this->serverParams->hasPostMaxSizeBeenExceeded()) {
|
||||||
$maxContentLength = $this->serverParams->getPostMaxSize();
|
|
||||||
|
|
||||||
if (!empty($maxContentLength) && $contentLength > $maxContentLength) {
|
|
||||||
// Submit the form, but don't clear the default values
|
// Submit the form, but don't clear the default values
|
||||||
$form->submit(null, false);
|
$form->submit(null, false);
|
||||||
|
|
||||||
|
@ -81,10 +81,7 @@ class NativeRequestHandler implements RequestHandlerInterface
|
|||||||
// Mark the form with an error if the uploaded size was too large
|
// Mark the form with an error if the uploaded size was too large
|
||||||
// This is done here and not in FormValidator because $_POST is
|
// This is done here and not in FormValidator because $_POST is
|
||||||
// empty when that error occurs. Hence the form is never submitted.
|
// empty when that error occurs. Hence the form is never submitted.
|
||||||
$contentLength = $this->serverParams->getContentLength();
|
if ($this->serverParams->hasPostMaxSizeBeenExceeded()) {
|
||||||
$maxContentLength = $this->serverParams->getPostMaxSize();
|
|
||||||
|
|
||||||
if (!empty($maxContentLength) && $contentLength > $maxContentLength) {
|
|
||||||
// Submit the form, but don't clear the default values
|
// Submit the form, but don't clear the default values
|
||||||
$form->submit(null, false);
|
$form->submit(null, false);
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener;
|
namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\Form;
|
||||||
use Symfony\Component\Form\FormBuilder;
|
use Symfony\Component\Form\FormBuilder;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
|
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener;
|
||||||
@ -72,4 +73,25 @@ class CsrfValidationListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
// Validate accordingly
|
// Validate accordingly
|
||||||
$this->assertSame($data, $event->getData());
|
$this->assertSame($data, $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMaxPostSizeExceeded()
|
||||||
|
{
|
||||||
|
$serverParams = $this
|
||||||
|
->getMockBuilder('\Symfony\Component\Form\Util\ServerParams')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock()
|
||||||
|
;
|
||||||
|
|
||||||
|
$serverParams
|
||||||
|
->expects($this->once())
|
||||||
|
->method('hasPostMaxSizeBeenExceeded')
|
||||||
|
->willReturn(true)
|
||||||
|
;
|
||||||
|
|
||||||
|
$event = new FormEvent($this->form, array('csrf' => 'token'));
|
||||||
|
$validation = new CsrfValidationListener('csrf', $this->tokenManager, 'unknown', 'Error message', null, null, $serverParams);
|
||||||
|
|
||||||
|
$validation->preSubmit($event);
|
||||||
|
$this->assertEmpty($this->form->getErrors());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,19 @@ class ServerParams
|
|||||||
$this->requestStack = $requestStack;
|
$this->requestStack = $requestStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the POST max size has been exceeded in the request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasPostMaxSizeBeenExceeded()
|
||||||
|
{
|
||||||
|
$contentLength = $this->getContentLength();
|
||||||
|
$maxContentLength = $this->getPostMaxSize();
|
||||||
|
|
||||||
|
return $maxContentLength && $contentLength > $maxContentLength;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns maximum post size in bytes.
|
* Returns maximum post size in bytes.
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user