summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreffulgentsia2015-10-03 07:26:32 -0700
committereffulgentsia2015-10-03 07:26:32 -0700
commit804d87927ed3fa33d8a7fabb6c1d2e11fe89269b (patch)
tree7885a63b71af761f4ee2408f021045d5a5331e55
parent13588e3a2e56657503c3efa2b9d5c3f3685dd841 (diff)
Issue #2463567 by borisson_, Wim Leers, Xano: Push CSRF tokens for forms to placeholders + #lazy_builder
-rw-r--r--core/lib/Drupal/Core/Form/FormBuilder.php39
-rw-r--r--core/modules/block_content/src/Tests/BlockContentTranslationUITest.php1
-rw-r--r--core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php13
-rw-r--r--core/modules/comment/src/Tests/CommentTranslationUITest.php1
-rw-r--r--core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php1
-rw-r--r--core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php2
-rw-r--r--core/modules/node/src/Tests/NodeBlockFunctionalTest.php2
-rw-r--r--core/modules/node/src/Tests/NodeTranslationUITest.php1
-rw-r--r--core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php2
-rw-r--r--core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php5
10 files changed, 58 insertions, 9 deletions
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 721ee92..bb7034f 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -652,6 +652,27 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
}
/**
+ * #lazy_builder callback; renders form CSRF token.
+ *
+ * @param string $placeholder
+ * A string containing a placeholder, matching the value of the form's
+ * #token.
+ *
+ * @return array
+ * A renderable array containing the CSRF token.
+ */
+ public function renderFormTokenPlaceholder($placeholder) {
+ return [
+ '#markup' => $this->csrfToken->get($placeholder),
+ '#cache' => [
+ 'contexts' => [
+ 'session',
+ ],
+ ],
+ ];
+ }
+
+ /**
* {@inheritdoc}
*/
public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
@@ -725,15 +746,29 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
$form['#cache']['contexts'][] = 'user.roles:authenticated';
if ($user && $user->isAuthenticated()) {
// Generate a public token based on the form id.
- $form['#token'] = $form_id;
+ // Generates a placeholder based on the form ID.
+ $placeholder = 'form_token_placeholder_' . hash('crc32b', $form_id);
+ $form['#token'] = $placeholder;
+
$form['form_token'] = array(
'#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
'#type' => 'token',
- '#default_value' => $this->csrfToken->get($form['#token']),
+ '#default_value' => $placeholder,
// 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'),
+ // Instead of setting an actual CSRF token, we've set the placeholder
+ // in form_token's #default_value and #placeholder. These will be
+ // replaced at the very last moment. This ensures forms with a CSRF
+ // token don't have poor cacheability.
+ '#attached' => [
+ 'placeholders' => [
+ $placeholder => [
+ '#lazy_builder' => ['form_builder:renderFormTokenPlaceholder', [$placeholder]]
+ ]
+ ]
+ ],
'#cache' => [
'max-age' => 0,
],
diff --git a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
index 91ccfda..616c3a2 100644
--- a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
+++ b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
@@ -35,6 +35,7 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
*/
protected $defaultCacheContexts = [
'languages:language_interface',
+ 'session',
'theme',
'url.path',
'url.query_args',
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index 21f2065..2c9a867 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -11,6 +11,9 @@ use Drupal\Core\Cache\Cache;
use Drupal\Core\Session\UserSession;
use Drupal\comment\CommentInterface;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpFoundation\Session\Session;
/**
* Tests the bubbling up of comment cache tags when using the Comment list
@@ -35,6 +38,16 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
protected function setUp() {
parent::setUp();
+ $session = new Session();
+
+ $request = Request::create('/');
+ $request->setSession($session);
+
+ /** @var RequestStack $stack */
+ $stack = $this->container->get('request_stack');
+ $stack->pop();
+ $stack->push($request);
+
// Set the current user to one that can access comments. Specifically, this
// user does not have access to the 'administer comments' permission, to
// ensure only published comments are visible to the end user.
diff --git a/core/modules/comment/src/Tests/CommentTranslationUITest.php b/core/modules/comment/src/Tests/CommentTranslationUITest.php
index 88c1d2f..26cfa90 100644
--- a/core/modules/comment/src/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/src/Tests/CommentTranslationUITest.php
@@ -37,6 +37,7 @@ class CommentTranslationUITest extends ContentTranslationUITestBase {
*/
protected $defaultCacheContexts = [
'languages:language_interface',
+ 'session',
'theme',
'timezone',
'url.query_args:_wrapper_format',
diff --git a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
index 08b1d4a..ce9237c 100644
--- a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
+++ b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
@@ -31,6 +31,7 @@ class ContentTestTranslationUITest extends ContentTranslationUITestBase {
*/
protected $defaultCacheContexts = [
'languages:language_interface',
+ 'session',
'theme',
'url.path',
'url.query_args',
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
index 1404384..fa61fb3 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
@@ -20,7 +20,7 @@ class MenuLinkContentTranslationUITest extends ContentTranslationUITestBase {
/**
* {inheritdoc}
*/
- protected $defaultCacheContexts = ['languages:language_interface', 'theme', 'url.path', 'url.query_args', 'user.permissions', 'user.roles:authenticated'];
+ protected $defaultCacheContexts = ['languages:language_interface', 'session', 'theme', 'url.path', 'url.query_args', 'user.permissions', 'user.roles:authenticated'];
/**
* Modules to enable.
diff --git a/core/modules/node/src/Tests/NodeBlockFunctionalTest.php b/core/modules/node/src/Tests/NodeBlockFunctionalTest.php
index b6855ba..386188f 100644
--- a/core/modules/node/src/Tests/NodeBlockFunctionalTest.php
+++ b/core/modules/node/src/Tests/NodeBlockFunctionalTest.php
@@ -148,7 +148,7 @@ class NodeBlockFunctionalTest extends NodeTestBase {
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route']);
$this->drupalGet('node/add/article');
$this->assertText($label, 'Block was displayed on the node/add/article page.');
- $this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.path', 'url.query_args', 'user', 'route']);
+ $this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'session', 'theme', 'url.path', 'url.query_args', 'user', 'route']);
$this->drupalGet('node/' . $node1->id());
$this->assertText($label, 'Block was displayed on the node/N when node is of type article.');
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php
index 509a0d1..490f5b1 100644
--- a/core/modules/node/src/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
@@ -26,6 +26,7 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
*/
protected $defaultCacheContexts = [
'languages:language_interface',
+ 'session',
'theme',
'route',
'timezone',
diff --git a/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php b/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php
index e42262f..9b09a37 100644
--- a/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutTranslationUITest.php
@@ -21,7 +21,7 @@ class ShortcutTranslationUITest extends ContentTranslationUITestBase {
/**
* {inheritdoc}
*/
- protected $defaultCacheContexts = ['languages:language_interface', 'theme', 'user', 'url.path', 'url.query_args', 'url.site'];
+ protected $defaultCacheContexts = ['languages:language_interface', 'session', 'theme', 'user', 'url.path', 'url.query_args', 'url.site'];
/**
* Modules to enable.
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index e5f2963..7bd51bc 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -783,10 +783,7 @@ class FormBuilderTest extends FormTestBase {
->willReturnArgument(0);
$this->csrfToken->expects($this->atLeastOnce())
->method('validate')
- ->will($this->returnValueMap([
- [$form_token, $form_id, $valid_token],
- [$form_id, $form_id, $valid_token],
- ]));
+ ->willReturn($valid_token);
}
$current_user = $this->prophesize(AccountInterface::class);