annotate core/modules/search/tests/src/Functional/SearchCommentTest.php @ 4:a9cd425dd02b

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:11:55 +0000
parents
children
rev   line source
Chris@4 1 <?php
Chris@4 2
Chris@4 3 namespace Drupal\Tests\search\Functional;
Chris@4 4
Chris@4 5 use Behat\Mink\Exception\ResponseTextException;
Chris@4 6 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
Chris@4 7 use Drupal\comment\Tests\CommentTestTrait;
Chris@4 8 use Drupal\field\Entity\FieldConfig;
Chris@4 9 use Drupal\Tests\BrowserTestBase;
Chris@4 10 use Drupal\Tests\Traits\Core\CronRunTrait;
Chris@4 11 use Drupal\user\RoleInterface;
Chris@4 12 use Drupal\filter\Entity\FilterFormat;
Chris@4 13
Chris@4 14 /**
Chris@4 15 * Tests integration searching comments.
Chris@4 16 *
Chris@4 17 * @group search
Chris@4 18 */
Chris@4 19 class SearchCommentTest extends BrowserTestBase {
Chris@4 20
Chris@4 21 use CommentTestTrait;
Chris@4 22 use CronRunTrait;
Chris@4 23
Chris@4 24 /**
Chris@4 25 * {@inheritdoc}
Chris@4 26 */
Chris@4 27 protected static $modules = ['filter', 'node', 'comment', 'search'];
Chris@4 28
Chris@4 29 /**
Chris@4 30 * Test subject for comments.
Chris@4 31 *
Chris@4 32 * @var string
Chris@4 33 */
Chris@4 34 protected $commentSubject;
Chris@4 35
Chris@4 36 /**
Chris@4 37 * ID for the administrator role.
Chris@4 38 *
Chris@4 39 * @var string
Chris@4 40 */
Chris@4 41 protected $adminRole;
Chris@4 42
Chris@4 43 /**
Chris@4 44 * A user with various administrative permissions.
Chris@4 45 *
Chris@4 46 * @var \Drupal\user\UserInterface
Chris@4 47 */
Chris@4 48 protected $adminUser;
Chris@4 49
Chris@4 50 /**
Chris@4 51 * Test node for searching.
Chris@4 52 *
Chris@4 53 * @var \Drupal\node\NodeInterface
Chris@4 54 */
Chris@4 55 protected $node;
Chris@4 56
Chris@4 57 protected function setUp() {
Chris@4 58 parent::setUp();
Chris@4 59
Chris@4 60 $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
Chris@4 61 $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
Chris@4 62
Chris@4 63 $full_html_format = FilterFormat::create([
Chris@4 64 'format' => 'full_html',
Chris@4 65 'name' => 'Full HTML',
Chris@4 66 'weight' => 1,
Chris@4 67 'filters' => [],
Chris@4 68 ]);
Chris@4 69 $full_html_format->save();
Chris@4 70
Chris@4 71 // Create and log in an administrative user having access to the Full HTML
Chris@4 72 // text format.
Chris@4 73 $permissions = [
Chris@4 74 'administer filters',
Chris@4 75 $full_html_format->getPermissionName(),
Chris@4 76 'administer permissions',
Chris@4 77 'create page content',
Chris@4 78 'post comments',
Chris@4 79 'skip comment approval',
Chris@4 80 'access comments',
Chris@4 81 ];
Chris@4 82 $this->adminUser = $this->drupalCreateUser($permissions);
Chris@4 83 $this->drupalLogin($this->adminUser);
Chris@4 84 // Add a comment field.
Chris@4 85 $this->addDefaultCommentField('node', 'article');
Chris@4 86 }
Chris@4 87
Chris@4 88 /**
Chris@4 89 * Verify that comments are rendered using proper format in search results.
Chris@4 90 */
Chris@4 91 public function testSearchResultsComment() {
Chris@4 92 $node_storage = $this->container->get('entity.manager')->getStorage('node');
Chris@4 93 // Create basic_html format that escapes all HTML.
Chris@4 94 $basic_html_format = FilterFormat::create([
Chris@4 95 'format' => 'basic_html',
Chris@4 96 'name' => 'Basic HTML',
Chris@4 97 'weight' => 1,
Chris@4 98 'filters' => [
Chris@4 99 'filter_html_escape' => ['status' => 1],
Chris@4 100 ],
Chris@4 101 'roles' => [RoleInterface::AUTHENTICATED_ID],
Chris@4 102 ]);
Chris@4 103 $basic_html_format->save();
Chris@4 104
Chris@4 105 $comment_body = 'Test comment body';
Chris@4 106
Chris@4 107 // Make preview optional.
Chris@4 108 $field = FieldConfig::loadByName('node', 'article', 'comment');
Chris@4 109 $field->setSetting('preview', DRUPAL_OPTIONAL);
Chris@4 110 $field->save();
Chris@4 111
Chris@4 112 // Allow anonymous users to search content.
Chris@4 113 $edit = [
Chris@4 114 RoleInterface::ANONYMOUS_ID . '[search content]' => 1,
Chris@4 115 RoleInterface::ANONYMOUS_ID . '[access comments]' => 1,
Chris@4 116 RoleInterface::ANONYMOUS_ID . '[post comments]' => 1,
Chris@4 117 ];
Chris@4 118 $this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
Chris@4 119
Chris@4 120 // Create a node.
Chris@4 121 $node = $this->drupalCreateNode(['type' => 'article']);
Chris@4 122 // Post a comment using 'Full HTML' text format.
Chris@4 123 $edit_comment = [];
Chris@4 124 $edit_comment['subject[0][value]'] = 'Test comment subject';
Chris@4 125 $edit_comment['comment_body[0][value]'] = '<h1>' . $comment_body . '</h1>';
Chris@4 126 $full_html_format_id = 'full_html';
Chris@4 127 $edit_comment['comment_body[0][format]'] = $full_html_format_id;
Chris@4 128 $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment, t('Save'));
Chris@4 129
Chris@4 130 // Post a comment with an evil script tag in the comment subject and a
Chris@4 131 // script tag nearby a keyword in the comment body. Use the 'FULL HTML' text
Chris@4 132 // format so the script tag stored.
Chris@4 133 $edit_comment2 = [];
Chris@4 134 $edit_comment2['subject[0][value]'] = "<script>alert('subjectkeyword');</script>";
Chris@4 135 $edit_comment2['comment_body[0][value]'] = "nearbykeyword<script>alert('somethinggeneric');</script>";
Chris@4 136 $edit_comment2['comment_body[0][format]'] = $full_html_format_id;
Chris@4 137 $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment2, t('Save'));
Chris@4 138
Chris@4 139 // Post a comment with a keyword inside an evil script tag in the comment
Chris@4 140 // body. Use the 'FULL HTML' text format so the script tag is stored.
Chris@4 141 $edit_comment3 = [];
Chris@4 142 $edit_comment3['subject[0][value]'] = 'asubject';
Chris@4 143 $edit_comment3['comment_body[0][value]'] = "<script>alert('insidekeyword');</script>";
Chris@4 144 $edit_comment3['comment_body[0][format]'] = $full_html_format_id;
Chris@4 145 $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment3, t('Save'));
Chris@4 146
Chris@4 147 // Invoke search index update.
Chris@4 148 $this->drupalLogout();
Chris@4 149 $this->cronRun();
Chris@4 150
Chris@4 151 // Search for the comment subject.
Chris@4 152 $edit = [
Chris@4 153 'keys' => "'" . $edit_comment['subject[0][value]'] . "'",
Chris@4 154 ];
Chris@4 155 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@4 156 $node_storage->resetCache([$node->id()]);
Chris@4 157 $node2 = $node_storage->load($node->id());
Chris@4 158 $this->assertText($node2->label(), 'Node found in search results.');
Chris@4 159 $this->assertText($edit_comment['subject[0][value]'], 'Comment subject found in search results.');
Chris@4 160
Chris@4 161 // Search for the comment body.
Chris@4 162 $edit = [
Chris@4 163 'keys' => "'" . $comment_body . "'",
Chris@4 164 ];
Chris@4 165 $this->drupalPostForm(NULL, $edit, t('Search'));
Chris@4 166 $this->assertText($node2->label(), 'Node found in search results.');
Chris@4 167
Chris@4 168 // Verify that comment is rendered using proper format.
Chris@4 169 $this->assertText($comment_body, 'Comment body text found in search results.');
Chris@4 170 $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.');
Chris@4 171 $this->assertNoEscaped($edit_comment['comment_body[0][value]'], 'HTML in comment body is not escaped.');
Chris@4 172
Chris@4 173 // Search for the evil script comment subject.
Chris@4 174 $edit = [
Chris@4 175 'keys' => 'subjectkeyword',
Chris@4 176 ];
Chris@4 177 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@4 178
Chris@4 179 // Verify the evil comment subject is escaped in search results.
Chris@4 180 $this->assertRaw('&lt;script&gt;alert(&#039;<strong>subjectkeyword</strong>&#039;);');
Chris@4 181 $this->assertNoRaw('<script>');
Chris@4 182
Chris@4 183 // Search for the keyword near the evil script tag in the comment body.
Chris@4 184 $edit = [
Chris@4 185 'keys' => 'nearbykeyword',
Chris@4 186 ];
Chris@4 187 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@4 188
Chris@4 189 // Verify that nearby script tag in the evil comment body is stripped from
Chris@4 190 // search results.
Chris@4 191 $this->assertRaw('<strong>nearbykeyword</strong>');
Chris@4 192 $this->assertNoRaw('<script>');
Chris@4 193
Chris@4 194 // Search for contents inside the evil script tag in the comment body.
Chris@4 195 $edit = [
Chris@4 196 'keys' => 'insidekeyword',
Chris@4 197 ];
Chris@4 198 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@4 199
Chris@4 200 // @todo Verify the actual search results.
Chris@4 201 // https://www.drupal.org/node/2551135
Chris@4 202
Chris@4 203 // Verify there is no script tag in search results.
Chris@4 204 $this->assertNoRaw('<script>');
Chris@4 205
Chris@4 206 // Hide comments.
Chris@4 207 $this->drupalLogin($this->adminUser);
Chris@4 208 $node->set('comment', CommentItemInterface::HIDDEN);
Chris@4 209 $node->save();
Chris@4 210
Chris@4 211 // Invoke search index update.
Chris@4 212 $this->drupalLogout();
Chris@4 213 $this->cronRun();
Chris@4 214
Chris@4 215 // Search for $title.
Chris@4 216 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@4 217 $this->assertText(t('Your search yielded no results.'));
Chris@4 218 }
Chris@4 219
Chris@4 220 /**
Chris@4 221 * Verify access rules for comment indexing with different permissions.
Chris@4 222 */
Chris@4 223 public function testSearchResultsCommentAccess() {
Chris@4 224 $comment_body = 'Test comment body';
Chris@4 225 $this->commentSubject = 'Test comment subject';
Chris@4 226 $roles = $this->adminUser->getRoles(TRUE);
Chris@4 227 $this->adminRole = $roles[0];
Chris@4 228
Chris@4 229 // Create a node.
Chris@4 230 // Make preview optional.
Chris@4 231 $field = FieldConfig::loadByName('node', 'article', 'comment');
Chris@4 232 $field->setSetting('preview', DRUPAL_OPTIONAL);
Chris@4 233 $field->save();
Chris@4 234 $this->node = $this->drupalCreateNode(['type' => 'article']);
Chris@4 235
Chris@4 236 // Post a comment using 'Full HTML' text format.
Chris@4 237 $edit_comment = [];
Chris@4 238 $edit_comment['subject[0][value]'] = $this->commentSubject;
Chris@4 239 $edit_comment['comment_body[0][value]'] = '<h1>' . $comment_body . '</h1>';
Chris@4 240 $this->drupalPostForm('comment/reply/node/' . $this->node->id() . '/comment', $edit_comment, t('Save'));
Chris@4 241
Chris@4 242 $this->drupalLogout();
Chris@4 243 $this->setRolePermissions(RoleInterface::ANONYMOUS_ID);
Chris@4 244 $this->assertCommentAccess(FALSE, 'Anon user has search permission but no access comments permission, comments should not be indexed');
Chris@4 245
Chris@4 246 $this->setRolePermissions(RoleInterface::ANONYMOUS_ID, TRUE);
Chris@4 247 $this->assertCommentAccess(TRUE, 'Anon user has search permission and access comments permission, comments should be indexed');
Chris@4 248
Chris@4 249 $this->drupalLogin($this->adminUser);
Chris@4 250 $this->drupalGet('admin/people/permissions');
Chris@4 251
Chris@4 252 // Disable search access for authenticated user to test admin user.
Chris@4 253 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, FALSE, FALSE);
Chris@4 254
Chris@4 255 $this->setRolePermissions($this->adminRole);
Chris@4 256 $this->assertCommentAccess(FALSE, 'Admin user has search permission but no access comments permission, comments should not be indexed');
Chris@4 257
Chris@4 258 $this->drupalGet('node/' . $this->node->id());
Chris@4 259 $this->setRolePermissions($this->adminRole, TRUE);
Chris@4 260 $this->assertCommentAccess(TRUE, 'Admin user has search permission and access comments permission, comments should be indexed');
Chris@4 261
Chris@4 262 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID);
Chris@4 263 $this->assertCommentAccess(FALSE, 'Authenticated user has search permission but no access comments permission, comments should not be indexed');
Chris@4 264
Chris@4 265 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, TRUE);
Chris@4 266 $this->assertCommentAccess(TRUE, 'Authenticated user has search permission and access comments permission, comments should be indexed');
Chris@4 267
Chris@4 268 // Verify that access comments permission is inherited from the
Chris@4 269 // authenticated role.
Chris@4 270 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, TRUE, FALSE);
Chris@4 271 $this->setRolePermissions($this->adminRole);
Chris@4 272 $this->assertCommentAccess(TRUE, 'Admin user has search permission and no access comments permission, but comments should be indexed because admin user inherits authenticated user\'s permission to access comments');
Chris@4 273
Chris@4 274 // Verify that search content permission is inherited from the authenticated
Chris@4 275 // role.
Chris@4 276 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, TRUE, TRUE);
Chris@4 277 $this->setRolePermissions($this->adminRole, TRUE, FALSE);
Chris@4 278 $this->assertCommentAccess(TRUE, 'Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search');
Chris@4 279 }
Chris@4 280
Chris@4 281 /**
Chris@4 282 * Set permissions for role.
Chris@4 283 */
Chris@4 284 public function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) {
Chris@4 285 $permissions = [
Chris@4 286 'access comments' => $access_comments,
Chris@4 287 'search content' => $search_content,
Chris@4 288 ];
Chris@4 289 user_role_change_permissions($rid, $permissions);
Chris@4 290 }
Chris@4 291
Chris@4 292 /**
Chris@4 293 * Update search index and search for comment.
Chris@4 294 */
Chris@4 295 public function assertCommentAccess($assume_access, $message) {
Chris@4 296 // Invoke search index update.
Chris@4 297 search_mark_for_reindex('node_search', $this->node->id());
Chris@4 298 $this->cronRun();
Chris@4 299
Chris@4 300 // Search for the comment subject.
Chris@4 301 $edit = [
Chris@4 302 'keys' => "'" . $this->commentSubject . "'",
Chris@4 303 ];
Chris@4 304 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@4 305
Chris@4 306 try {
Chris@4 307 if ($assume_access) {
Chris@4 308 $this->assertSession()->pageTextContains($this->node->label());
Chris@4 309 $this->assertSession()->pageTextContains($this->commentSubject);
Chris@4 310 }
Chris@4 311 else {
Chris@4 312 $this->assertSession()->pageTextContains(t('Your search yielded no results.'));
Chris@4 313 }
Chris@4 314 }
Chris@4 315 catch (ResponseTextException $exception) {
Chris@4 316 $this->fail($message);
Chris@4 317 }
Chris@4 318 }
Chris@4 319
Chris@4 320 /**
Chris@4 321 * Verify that 'add new comment' does not appear in search results or index.
Chris@4 322 */
Chris@4 323 public function testAddNewComment() {
Chris@4 324 // Create a node with a short body.
Chris@4 325 $settings = [
Chris@4 326 'type' => 'article',
Chris@4 327 'title' => 'short title',
Chris@4 328 'body' => [['value' => 'short body text']],
Chris@4 329 ];
Chris@4 330
Chris@4 331 $user = $this->drupalCreateUser([
Chris@4 332 'search content',
Chris@4 333 'create article content',
Chris@4 334 'access content',
Chris@4 335 'post comments',
Chris@4 336 'access comments',
Chris@4 337 ]);
Chris@4 338 $this->drupalLogin($user);
Chris@4 339
Chris@4 340 $node = $this->drupalCreateNode($settings);
Chris@4 341 // Verify that if you view the node on its own page, 'add new comment'
Chris@4 342 // is there.
Chris@4 343 $this->drupalGet('node/' . $node->id());
Chris@4 344 $this->assertText(t('Add new comment'));
Chris@4 345
Chris@4 346 // Run cron to index this page.
Chris@4 347 $this->drupalLogout();
Chris@4 348 $this->cronRun();
Chris@4 349
Chris@4 350 // Search for 'comment'. Should be no results.
Chris@4 351 $this->drupalLogin($user);
Chris@4 352 $this->drupalPostForm('search/node', ['keys' => 'comment'], t('Search'));
Chris@4 353 $this->assertText(t('Your search yielded no results'));
Chris@4 354
Chris@4 355 // Search for the node title. Should be found, and 'Add new comment' should
Chris@4 356 // not be part of the search snippet.
Chris@4 357 $this->drupalPostForm('search/node', ['keys' => 'short'], t('Search'));
Chris@4 358 $this->assertText($node->label(), 'Search for keyword worked');
Chris@4 359 $this->assertNoText(t('Add new comment'));
Chris@4 360 }
Chris@4 361
Chris@4 362 }