diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index a9ae1bdcce3662cd36456da1493622a786c7b25e..b3410649545b19726604108c4e834e5c0aefa571 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -688,18 +688,24 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { if ($form_state->isProgrammed() || (isset($form['#token']) && $form['#token'] === FALSE)) { unset($form['#token']); } - elseif ($user && $user->isAuthenticated()) { - // Generate a public token based on the form id. - $form['#token'] = $form_id; - $form['form_token'] = array( - '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'), - '#type' => 'token', - '#default_value' => $this->csrfToken->get($form['#token']), - // Form processing and validation requires this value, so ensure the - // submitted form value appears literally, regardless of custom #tree - // and #parents being set elsewhere. - '#parents' => array('form_token'), - ); + else { + $form['#cache']['contexts'][] = 'user.roles:authenticated'; + if ($user && $user->isAuthenticated()) { + // Generate a public token based on the form id. + $form['#token'] = $form_id; + $form['form_token'] = array( + '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'), + '#type' => 'token', + '#default_value' => $this->csrfToken->get($form['#token']), + // Form processing and validation requires this value, so ensure the + // submitted form value appears literally, regardless of custom #tree + // and #parents being set elsewhere. + '#parents' => array('form_token'), + '#cache' => [ + 'max-age' => 0, + ], + ); + } } if (isset($form_id)) { diff --git a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php index d17fc3bf4edf445f7d9cfda6b20869a143ddb456..4ce15655badfe0d00d84ec075f9a6cdfed54a5a4 100644 --- a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php +++ b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php @@ -30,6 +30,17 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase { 'block_content' ); + /** + * {@inheritdoc} + */ + protected $defaultCacheContexts = [ + 'languages:language_interface', + 'theme', + 'url.query_args:_wrapper_format', + 'user.permissions', + 'user.roles:authenticated', + ]; + /** * Overrides \Drupal\simpletest\WebTestBase::setUp(). */ diff --git a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php index 3569a457d3c6f6c89cfdc91b3f07fb52b8cc2d00..43fd7798ed39ef136d32e0d78aeb1b4433d55cc0 100644 --- a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php +++ b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php @@ -26,6 +26,17 @@ class ContentTestTranslationUITest extends ContentTranslationUITestBase { */ public static $modules = array('language', 'content_translation', 'entity_test'); + /** + * {@inheritdoc} + */ + protected $defaultCacheContexts = [ + 'languages:language_interface', + 'theme', + 'url.query_args:_wrapper_format', + 'user.permissions', + 'user.roles:authenticated', + ]; + /** * Overrides \Drupal\simpletest\WebTestBase::setUp(). */ diff --git a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php index 1427c5de50816e24d229a69e9feb4a769ac3bb5a..f1ef4c51aa84a9aeed36e15849386abfabe863f3 100644 --- a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php +++ b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php @@ -207,6 +207,7 @@ public function testExposedSortAndItemsPerPage() { 'entity_test_view_grants', 'theme', 'url.query_args', + 'user.roles:authenticated', 'languages:language_content' ]; diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 86b1fcb281d4b9426a057825d61f4b10c76c5f6e..d95e3bcb6f64fbdca4d6bca8357411248c21cb5c 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -18,6 +18,7 @@ use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Session\AccountProxyInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -767,6 +768,65 @@ public function providerTestInvalidToken() { return $data; } + /** + * @covers ::prepareForm + * + * @dataProvider providerTestFormTokenCacheability + */ + function testFormTokenCacheability($token, $is_authenticated, $expected_form_cacheability, $expected_token_cacheability) { + $user = $this->prophesize(AccountProxyInterface::class); + $user->isAuthenticated() + ->willReturn($is_authenticated); + $this->container->set('current_user', $user->reveal()); + \Drupal::setContainer($this->container); + + $form_id = 'test_form_id'; + $form = $form_id(); + + if (isset($token)) { + $form['#token'] = $token; + } + + $form_arg = $this->getMock('Drupal\Core\Form\FormInterface'); + $form_arg->expects($this->once()) + ->method('getFormId') + ->will($this->returnValue($form_id)); + $form_arg->expects($this->once()) + ->method('buildForm') + ->will($this->returnValue($form)); + + $form_state = new FormState(); + $built_form = $this->formBuilder->buildForm($form_arg, $form_state); + if (!isset($expected_form_cacheability)) { + $this->assertFalse(isset($built_form['#cache'])); + } + else { + $this->assertTrue(isset($built_form['#cache'])); + $this->assertEquals($expected_form_cacheability, $built_form['#cache']); + } + if (!isset($expected_token_cacheability)) { + $this->assertFalse(isset($built_form['form_token'])); + } + else { + $this->assertTrue(isset($built_form['form_token'])); + $this->assertEquals($expected_token_cacheability, $built_form['form_token']['#cache']); + } + } + + /** + * Data provider for testFormTokenCacheability. + * + * @return array + */ + function providerTestFormTokenCacheability() { + return [ + 'token:none,authenticated:true' => [NULL, TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0]], + 'token:false,authenticated:true' => [FALSE, TRUE, NULL, NULL], + 'token:none,authenticated:false' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL], + 'token:false,authenticated:false' => [FALSE, FALSE, NULL, NULL], + ]; + } + } class TestForm implements FormInterface {