core/lib/Drupal/Core/Form/FormBuilder.php | 3 -- .../ckeditor/src/Tests/CKEditorLoadingTest.php | 2 + .../Tests/CommentDefaultFormatterCacheTagsTest.php | 2 + core/modules/contact/src/MessageForm.php | 3 ++ .../src/Tests/ContactAuthenticatedUserTest.php | 14 ++++++- core/modules/editor/src/Element.php | 1 + .../modules/editor/src/Tests/EditorLoadingTest.php | 2 + .../editor/src/Tests/EditorSecurityTest.php | 2 + core/modules/filter/src/Element/TextFormat.php | 2 + .../Drupal/Tests/Core/Form/FormBuilderTest.php | 45 ++++++++++++++-------- 10 files changed, 55 insertions(+), 21 deletions(-) diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index 721ee92..3dac78b 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -734,9 +734,6 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { // submitted form value appears literally, regardless of custom #tree // and #parents being set elsewhere. '#parents' => array('form_token'), - '#cache' => [ - 'max-age' => 0, - ], ); } } diff --git a/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php b/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php index 1081eb9..92e694e 100644 --- a/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php +++ b/core/modules/ckeditor/src/Tests/CKEditorLoadingTest.php @@ -7,6 +7,7 @@ namespace Drupal\ckeditor\Tests; +use Drupal\Core\Cache\Cache; use Drupal\simpletest\WebTestBase; /** @@ -126,6 +127,7 @@ function testLoading() { // configuration also results in modified CKEditor configuration, so we // don't test that here. \Drupal::service('module_installer')->install(array('ckeditor_test')); + Cache::invalidateTags(['rendered']); $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions(); $editor_settings = $editor->getSettings(); $editor_settings['toolbar']['rows'][0][0]['items'][] = 'Llama'; diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php index 327a354..4346827 100644 --- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php +++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php @@ -78,6 +78,7 @@ public function testCacheTags() { 'config:field.field.entity_test.entity_test.comment', 'config:field.storage.comment.comment_body', 'config:user.settings', + 'user:1', ]; sort($expected_cache_tags); $this->assertEqual($build['#cache']['tags'], $expected_cache_tags); @@ -124,6 +125,7 @@ public function testCacheTags() { 'config:field.field.entity_test.entity_test.comment', 'config:field.storage.comment.comment_body', 'config:user.settings', + 'user:1', ]; sort($expected_cache_tags); $this->assertEqual($build['#cache']['tags'], $expected_cache_tags); diff --git a/core/modules/contact/src/MessageForm.php b/core/modules/contact/src/MessageForm.php index 9cb5e54..c576a56 100644 --- a/core/modules/contact/src/MessageForm.php +++ b/core/modules/contact/src/MessageForm.php @@ -120,6 +120,7 @@ public function form(array $form, FormStateInterface $form_state) { '#title' => $this->t('Your email address'), '#required' => TRUE, ); + $form['#cache']['contexts'][] = 'user.roles:authenticated'; if ($user->isAnonymous()) { $form['#attached']['library'][] = 'core/drupal.form'; $form['#attributes']['data-user-info-from-browser'] = TRUE; @@ -131,11 +132,13 @@ public function form(array $form, FormStateInterface $form_state) { $form['name']['#value'] = $user->getUsername(); $form['name']['#required'] = FALSE; $form['name']['#plain_text'] = $user->getUsername(); + $form['name']['#cache']['contexts'][] = 'user'; $form['mail']['#type'] = 'item'; $form['mail']['#value'] = $user->getEmail(); $form['mail']['#required'] = FALSE; $form['mail']['#plain_text'] = $user->getEmail(); + $form['mail']['#cache']['contexts'][] = 'user'; } // The user contact form has a preset recipient. diff --git a/core/modules/contact/src/Tests/ContactAuthenticatedUserTest.php b/core/modules/contact/src/Tests/ContactAuthenticatedUserTest.php index 2adc4c2..923878e 100644 --- a/core/modules/contact/src/Tests/ContactAuthenticatedUserTest.php +++ b/core/modules/contact/src/Tests/ContactAuthenticatedUserTest.php @@ -21,19 +21,29 @@ class ContactAuthenticatedUserTest extends WebTestBase { * * @var array */ - public static $modules = array('contact'); + public static $modules = array('contact', 'contact_test'); /** * Tests that name and email fields are not present for authenticated users. */ function testContactSiteWideTextfieldsLoggedInTestCase() { - $this->drupalLogin($this->drupalCreateUser(array('access site-wide contact form'))); + $user1 = $this->drupalCreateUser(array('access site-wide contact form')); + $this->drupalLogin($user1); $this->drupalGet('contact'); + $this->assertResponse(200); + $this->assertCacheContext('user'); // Ensure that there is no textfield for name. $this->assertFalse($this->xpath('//input[@name=:name]', array(':name' => 'name'))); + $this->assertRaw($user1->getAccountName()); // Ensure that there is no textfield for email. $this->assertFalse($this->xpath('//input[@name=:name]', array(':name' => 'mail'))); + + // Log in as a different user and confirm that + $user2 = $this->drupalCreateUser(array('access site-wide contact form')); + $this->drupalLogin($user2); + $this->drupalGet('contact'); + $this->assertRaw($user2->getAccountName()); } } diff --git a/core/modules/editor/src/Element.php b/core/modules/editor/src/Element.php index 43b99ba..41ffedf 100644 --- a/core/modules/editor/src/Element.php +++ b/core/modules/editor/src/Element.php @@ -53,6 +53,7 @@ function preRenderTextFormat(array $element) { $format_ids = array_keys($element['format']['format']['#options']); // Early-return if no text editor is associated with any of the text formats. + $element['#cache']['tags'][] = 'config:editor_list'; $editors = Editor::loadMultiple($format_ids); foreach ($editors as $key => $editor) { $definition = $this->pluginManager->getDefinition($editor->getEditor()); diff --git a/core/modules/editor/src/Tests/EditorLoadingTest.php b/core/modules/editor/src/Tests/EditorLoadingTest.php index 002ccd3..95dda17 100644 --- a/core/modules/editor/src/Tests/EditorLoadingTest.php +++ b/core/modules/editor/src/Tests/EditorLoadingTest.php @@ -245,6 +245,7 @@ public function testSupportedElementTypes() { // Assert the unicorn editor works with textfields. $this->drupalLogin($this->privilegedUser); $this->drupalGet('node/1/edit'); + $this->assertCacheTag('config:editor_list'); list( , $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input'); $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page."); $this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.'); @@ -261,6 +262,7 @@ public function testSupportedElementTypes() { ))->save(); $this->drupalGet('node/1/edit'); + $this->assertCacheTag('config:editor_list'); list( , $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input'); $this->assertFalse($editor_settings_present, "Text Editor module's JavaScript settings are not on the page."); $this->assertFalse($editor_js_present, 'Text Editor JavaScript is not present.'); diff --git a/core/modules/editor/src/Tests/EditorSecurityTest.php b/core/modules/editor/src/Tests/EditorSecurityTest.php index bb39450..db15b05 100644 --- a/core/modules/editor/src/Tests/EditorSecurityTest.php +++ b/core/modules/editor/src/Tests/EditorSecurityTest.php @@ -8,6 +8,7 @@ namespace Drupal\editor\Tests; use Drupal\Component\Serialization\Json; +use Drupal\Core\Cache\Cache; use Drupal\simpletest\WebTestBase; /** @@ -428,6 +429,7 @@ function testEditorXssFilterOverride() { // Enable editor_test.module's hook_editor_xss_filter_alter() implementation // to alter the text editor XSS filter class being used. \Drupal::state()->set('editor_test_editor_xss_filter_alter_enabled', TRUE); + Cache::invalidateTags(['rendered']); // First: the Insecure text editor XSS filter. $this->drupalGet('node/2/edit'); diff --git a/core/modules/filter/src/Element/TextFormat.php b/core/modules/filter/src/Element/TextFormat.php index 9247dba..9b3000f 100644 --- a/core/modules/filter/src/Element/TextFormat.php +++ b/core/modules/filter/src/Element/TextFormat.php @@ -210,6 +210,8 @@ public static function processFormat(&$element, FormStateInterface $form_state, $user_has_access = isset($formats[$element['#format']]); $user_is_admin = $user->hasPermission('administer filters'); + $element['#cache']['tags'][] = 'config:filter_format_list'; + // If the stored format does not exist or if it is not among the allowed // formats for this textarea, administrators have to assign a new format. if ((!$format_exists || !$format_allowed) && $user_is_admin) { diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index e5f2963..f37a4c7 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -820,7 +820,7 @@ public function providerTestInvalidToken() { * * @dataProvider providerTestFormTokenCacheability */ - public function testFormTokenCacheability($token, $is_authenticated, $expected_form_cacheability, $expected_token_cacheability, $method) { + public function testFormTokenCacheability($token, $is_authenticated, $method) { $user = $this->prophesize(AccountProxyInterface::class); $user->isAuthenticated() ->willReturn($is_authenticated); @@ -845,19 +845,32 @@ public function testFormTokenCacheability($token, $is_authenticated, $expected_f $form_state = new FormState(); $built_form = $this->formBuilder->buildForm($form_arg, $form_state); - if (!isset($expected_form_cacheability) || ($method == 'get' && !is_string($token))) { + + // FormBuilder does not even consider to set a form token when: + // - #token = FALSE (opting out explicitly) + // - #method = GET and #token is not set to a string (GET forms don't get a + // form token by default, and this form did not explicitly opt in) + if ($token === FALSE || ($method == 'get' && !is_string($token))) { $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'])); } + // Otherwise, a form token is set, but only if the user is logged in. It is + // impossible (and unnecessary) to set a form token if the user is not + // logged in, because there is no session, and hence no CSRF token. else { - $this->assertTrue(isset($built_form['form_token'])); - $this->assertEquals($expected_token_cacheability, $built_form['form_token']['#cache']); + // For forms that are eligible for form tokens, a cache context must be + // set that indicates the form token only exists for logged in users. + $this->assertTrue(isset($built_form['#cache'])); + $this->assertEquals(['contexts' => ['user.roles:authenticated']], $built_form['#cache']); + // Finally, verify that a form token is generated when appropriate, with + // the expected cacheability metadata (or lack thereof). + if (!$is_authenticated) { + $this->assertFalse(isset($built_form['form_token'])); + } + else { + $this->assertTrue(isset($built_form['form_token'])); + $this->assertFalse(isset($built_form['form_token']['#cache'])); + } } } @@ -868,12 +881,12 @@ public function testFormTokenCacheability($token, $is_authenticated, $expected_f */ function providerTestFormTokenCacheability() { return [ - 'token:none,authenticated:true' => [NULL, TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0], 'post'], - 'token:none,authenticated:false' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL, 'post'], - 'token:false,authenticated:false' => [FALSE, FALSE, NULL, NULL, 'post'], - 'token:false,authenticated:true' => [FALSE, TRUE, NULL, NULL, 'post'], - 'token:none,authenticated:false,method:get' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL, 'get'], - 'token:test_form_id,authenticated:false,method:get' => ['test_form_id', TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0], 'get'], + 'token:none,authenticated:true' => [NULL, TRUE, 'post'], + 'token:none,authenticated:false' => [NULL, FALSE, 'post'], + 'token:false,authenticated:false' => [FALSE, FALSE, 'post'], + 'token:false,authenticated:true' => [FALSE, TRUE, 'post'], + 'token:none,authenticated:false,method:get' => [NULL, FALSE, 'get'], + 'token:test_form_id,authenticated:true,method:get' => ['test_form_id', TRUE, 'get'], ]; }