annotate core/modules/contextual/tests/src/Functional/ContextualDynamicContextTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@17 1 <?php
Chris@17 2
Chris@17 3 namespace Drupal\Tests\contextual\Functional;
Chris@17 4
Chris@17 5 use Drupal\Component\Serialization\Json;
Chris@17 6 use Drupal\Component\Utility\Crypt;
Chris@17 7 use Drupal\Core\Site\Settings;
Chris@17 8 use Drupal\Core\Url;
Chris@17 9 use Drupal\language\Entity\ConfigurableLanguage;
Chris@17 10 use Drupal\Tests\BrowserTestBase;
Chris@17 11
Chris@17 12 /**
Chris@17 13 * Tests if contextual links are showing on the front page depending on
Chris@17 14 * permissions.
Chris@17 15 *
Chris@17 16 * @group contextual
Chris@17 17 */
Chris@17 18 class ContextualDynamicContextTest extends BrowserTestBase {
Chris@17 19
Chris@17 20 /**
Chris@17 21 * A user with permission to access contextual links and edit content.
Chris@17 22 *
Chris@17 23 * @var \Drupal\user\UserInterface
Chris@17 24 */
Chris@17 25 protected $editorUser;
Chris@17 26
Chris@17 27 /**
Chris@17 28 * An authenticated user with permission to access contextual links.
Chris@17 29 *
Chris@17 30 * @var \Drupal\user\UserInterface
Chris@17 31 */
Chris@17 32 protected $authenticatedUser;
Chris@17 33
Chris@17 34 /**
Chris@17 35 * A simulated anonymous user with access only to node content.
Chris@17 36 *
Chris@17 37 * @var \Drupal\user\UserInterface
Chris@17 38 */
Chris@17 39 protected $anonymousUser;
Chris@17 40
Chris@17 41 /**
Chris@17 42 * Modules to enable.
Chris@17 43 *
Chris@17 44 * @var array
Chris@17 45 */
Chris@17 46 public static $modules = ['contextual', 'node', 'views', 'views_ui', 'language', 'menu_test'];
Chris@17 47
Chris@17 48 protected function setUp() {
Chris@17 49 parent::setUp();
Chris@17 50
Chris@17 51 $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
Chris@17 52 $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
Chris@17 53
Chris@17 54 ConfigurableLanguage::createFromLangcode('it')->save();
Chris@17 55 $this->rebuildContainer();
Chris@17 56
Chris@17 57 $this->editorUser = $this->drupalCreateUser(['access content', 'access contextual links', 'edit any article content']);
Chris@17 58 $this->authenticatedUser = $this->drupalCreateUser(['access content', 'access contextual links']);
Chris@17 59 $this->anonymousUser = $this->drupalCreateUser(['access content']);
Chris@17 60 }
Chris@17 61
Chris@17 62 /**
Chris@17 63 * Tests contextual links with different permissions.
Chris@17 64 *
Chris@17 65 * Ensures that contextual link placeholders always exist, even if the user is
Chris@17 66 * not allowed to use contextual links.
Chris@17 67 */
Chris@17 68 public function testDifferentPermissions() {
Chris@17 69 $this->drupalLogin($this->editorUser);
Chris@17 70
Chris@17 71 // Create three nodes in the following order:
Chris@17 72 // - An article, which should be user-editable.
Chris@17 73 // - A page, which should not be user-editable.
Chris@17 74 // - A second article, which should also be user-editable.
Chris@17 75 $node1 = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
Chris@17 76 $node2 = $this->drupalCreateNode(['type' => 'page', 'promote' => 1]);
Chris@17 77 $node3 = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
Chris@17 78
Chris@17 79 // Now, on the front page, all article nodes should have contextual links
Chris@17 80 // placeholders, as should the view that contains them.
Chris@17 81 $ids = [
Chris@17 82 'node:node=' . $node1->id() . ':changed=' . $node1->getChangedTime() . '&langcode=en',
Chris@17 83 'node:node=' . $node2->id() . ':changed=' . $node2->getChangedTime() . '&langcode=en',
Chris@17 84 'node:node=' . $node3->id() . ':changed=' . $node3->getChangedTime() . '&langcode=en',
Chris@17 85 'entity.view.edit_form:view=frontpage:location=page&name=frontpage&display_id=page_1&langcode=en',
Chris@17 86 ];
Chris@17 87
Chris@17 88 // Editor user: can access contextual links and can edit articles.
Chris@17 89 $this->drupalGet('node');
Chris@17 90 for ($i = 0; $i < count($ids); $i++) {
Chris@17 91 $this->assertContextualLinkPlaceHolder($ids[$i]);
Chris@17 92 }
Chris@17 93 $response = $this->renderContextualLinks([], 'node');
Chris@17 94 $this->assertSame(400, $response->getStatusCode());
Chris@17 95 $this->assertContains('No contextual ids specified.', (string) $response->getBody());
Chris@17 96 $response = $this->renderContextualLinks($ids, 'node');
Chris@17 97 $this->assertSame(200, $response->getStatusCode());
Chris@17 98 $json = Json::decode((string) $response->getBody());
Chris@17 99 $this->assertIdentical($json[$ids[0]], '<ul class="contextual-links"><li class="entitynodeedit-form"><a href="' . base_path() . 'node/1/edit">Edit</a></li></ul>');
Chris@17 100 $this->assertIdentical($json[$ids[1]], '');
Chris@17 101 $this->assertIdentical($json[$ids[2]], '<ul class="contextual-links"><li class="entitynodeedit-form"><a href="' . base_path() . 'node/3/edit">Edit</a></li></ul>');
Chris@17 102 $this->assertIdentical($json[$ids[3]], '');
Chris@17 103
Chris@17 104 // Verify that link language is properly handled.
Chris@17 105 $node3->addTranslation('it')->set('title', $this->randomString())->save();
Chris@17 106 $id = 'node:node=' . $node3->id() . ':changed=' . $node3->getChangedTime() . '&langcode=it';
Chris@17 107 $this->drupalGet('node', ['language' => ConfigurableLanguage::createFromLangcode('it')]);
Chris@17 108 $this->assertContextualLinkPlaceHolder($id);
Chris@17 109
Chris@17 110 // Authenticated user: can access contextual links, cannot edit articles.
Chris@17 111 $this->drupalLogin($this->authenticatedUser);
Chris@17 112 $this->drupalGet('node');
Chris@17 113 for ($i = 0; $i < count($ids); $i++) {
Chris@17 114 $this->assertContextualLinkPlaceHolder($ids[$i]);
Chris@17 115 }
Chris@17 116 $response = $this->renderContextualLinks([], 'node');
Chris@17 117 $this->assertSame(400, $response->getStatusCode());
Chris@17 118 $this->assertContains('No contextual ids specified.', (string) $response->getBody());
Chris@17 119 $response = $this->renderContextualLinks($ids, 'node');
Chris@17 120 $this->assertSame(200, $response->getStatusCode());
Chris@17 121 $json = Json::decode((string) $response->getBody());
Chris@17 122 $this->assertIdentical($json[$ids[0]], '');
Chris@17 123 $this->assertIdentical($json[$ids[1]], '');
Chris@17 124 $this->assertIdentical($json[$ids[2]], '');
Chris@17 125 $this->assertIdentical($json[$ids[3]], '');
Chris@17 126
Chris@17 127 // Anonymous user: cannot access contextual links.
Chris@17 128 $this->drupalLogin($this->anonymousUser);
Chris@17 129 $this->drupalGet('node');
Chris@17 130 for ($i = 0; $i < count($ids); $i++) {
Chris@17 131 $this->assertNoContextualLinkPlaceHolder($ids[$i]);
Chris@17 132 }
Chris@17 133 $response = $this->renderContextualLinks([], 'node');
Chris@17 134 $this->assertSame(403, $response->getStatusCode());
Chris@17 135 $this->renderContextualLinks($ids, 'node');
Chris@17 136 $this->assertSame(403, $response->getStatusCode());
Chris@17 137
Chris@17 138 // Get a page where contextual links are directly rendered.
Chris@17 139 $this->drupalGet(Url::fromRoute('menu_test.contextual_test'));
Chris@17 140 $this->assertEscaped("<script>alert('Welcome to the jungle!')</script>");
Chris@17 141 $this->assertRaw('<li class="menu-testcontextual-hidden-manage-edit"><a href="' . base_path() . 'menu-test-contextual/1/edit" class="use-ajax" data-dialog-type="modal" data-is-something>Edit menu - contextual</a></li>');
Chris@17 142 }
Chris@17 143
Chris@17 144 /**
Chris@17 145 * Tests the contextual placeholder content is protected by a token.
Chris@17 146 */
Chris@17 147 public function testTokenProtection() {
Chris@17 148 $this->drupalLogin($this->editorUser);
Chris@17 149
Chris@17 150 // Create a node that will have a contextual link.
Chris@17 151 $node1 = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
Chris@17 152
Chris@17 153 // Now, on the front page, all article nodes should have contextual links
Chris@17 154 // placeholders, as should the view that contains them.
Chris@17 155 $id = 'node:node=' . $node1->id() . ':changed=' . $node1->getChangedTime() . '&langcode=en';
Chris@17 156
Chris@17 157 // Editor user: can access contextual links and can edit articles.
Chris@17 158 $this->drupalGet('node');
Chris@17 159 $this->assertContextualLinkPlaceHolder($id);
Chris@17 160
Chris@17 161 $http_client = $this->getHttpClient();
Chris@17 162 $url = Url::fromRoute('contextual.render', [], [
Chris@17 163 'query' => [
Chris@17 164 '_format' => 'json',
Chris@17 165 'destination' => 'node',
Chris@17 166 ],
Chris@17 167 ])->setAbsolute()->toString();
Chris@17 168
Chris@17 169 $response = $http_client->request('POST', $url, [
Chris@17 170 'cookies' => $this->getSessionCookies(),
Chris@17 171 'form_params' => ['ids' => [$id], 'tokens' => []],
Chris@17 172 'http_errors' => FALSE,
Chris@17 173 ]);
Chris@17 174 $this->assertEquals('400', $response->getStatusCode());
Chris@17 175 $this->assertContains('No contextual ID tokens specified.', (string) $response->getBody());
Chris@17 176
Chris@17 177 $response = $http_client->request('POST', $url, [
Chris@17 178 'cookies' => $this->getSessionCookies(),
Chris@17 179 'form_params' => ['ids' => [$id], 'tokens' => ['wrong_token']],
Chris@17 180 'http_errors' => FALSE,
Chris@17 181 ]);
Chris@17 182 $this->assertEquals('400', $response->getStatusCode());
Chris@17 183 $this->assertContains('Invalid contextual ID specified.', (string) $response->getBody());
Chris@17 184
Chris@17 185 $response = $http_client->request('POST', $url, [
Chris@17 186 'cookies' => $this->getSessionCookies(),
Chris@17 187 'form_params' => ['ids' => [$id], 'tokens' => ['wrong_key' => $this->createContextualIdToken($id)]],
Chris@17 188 'http_errors' => FALSE,
Chris@17 189 ]);
Chris@17 190 $this->assertEquals('400', $response->getStatusCode());
Chris@17 191 $this->assertContains('Invalid contextual ID specified.', (string) $response->getBody());
Chris@17 192
Chris@17 193 $response = $http_client->request('POST', $url, [
Chris@17 194 'cookies' => $this->getSessionCookies(),
Chris@17 195 'form_params' => ['ids' => [$id], 'tokens' => [$this->createContextualIdToken($id)]],
Chris@17 196 'http_errors' => FALSE,
Chris@17 197 ]);
Chris@17 198 $this->assertEquals('200', $response->getStatusCode());
Chris@17 199 }
Chris@17 200
Chris@17 201 /**
Chris@17 202 * Asserts that a contextual link placeholder with the given id exists.
Chris@17 203 *
Chris@17 204 * @param string $id
Chris@17 205 * A contextual link id.
Chris@17 206 */
Chris@17 207 protected function assertContextualLinkPlaceHolder($id) {
Chris@17 208 $this->assertSession()->elementAttributeContains(
Chris@17 209 'css',
Chris@17 210 'div[data-contextual-id="' . $id . '"]',
Chris@17 211 'data-contextual-token',
Chris@17 212 $this->createContextualIdToken($id)
Chris@17 213 );
Chris@17 214 }
Chris@17 215
Chris@17 216 /**
Chris@17 217 * Asserts that a contextual link placeholder with the given id does not exist.
Chris@17 218 *
Chris@17 219 * @param string $id
Chris@17 220 * A contextual link id.
Chris@17 221 */
Chris@17 222 protected function assertNoContextualLinkPlaceHolder($id) {
Chris@17 223 $this->assertSession()->elementNotExists('css', 'div[data-contextual-id="' . $id . '"]');
Chris@17 224 }
Chris@17 225
Chris@17 226 /**
Chris@17 227 * Get server-rendered contextual links for the given contextual link ids.
Chris@17 228 *
Chris@17 229 * @param array $ids
Chris@17 230 * An array of contextual link ids.
Chris@17 231 * @param string $current_path
Chris@17 232 * The Drupal path for the page for which the contextual links are rendered.
Chris@17 233 *
Chris@17 234 * @return \Psr\Http\Message\ResponseInterface
Chris@17 235 * The response object.
Chris@17 236 */
Chris@17 237 protected function renderContextualLinks($ids, $current_path) {
Chris@17 238 $tokens = array_map([$this, 'createContextualIdToken'], $ids);
Chris@17 239 $http_client = $this->getHttpClient();
Chris@17 240 $url = Url::fromRoute('contextual.render', [], [
Chris@17 241 'query' => [
Chris@17 242 '_format' => 'json',
Chris@17 243 'destination' => $current_path,
Chris@17 244 ],
Chris@17 245 ]);
Chris@17 246
Chris@17 247 return $http_client->request('POST', $this->buildUrl($url), [
Chris@17 248 'cookies' => $this->getSessionCookies(),
Chris@17 249 'form_params' => ['ids' => $ids, 'tokens' => $tokens],
Chris@17 250 'http_errors' => FALSE,
Chris@17 251 ]);
Chris@17 252 }
Chris@17 253
Chris@17 254 /**
Chris@17 255 * Creates a contextual ID token.
Chris@17 256 *
Chris@17 257 * @param string $id
Chris@17 258 * The contextual ID to create a token for.
Chris@17 259 *
Chris@17 260 * @return string
Chris@17 261 * The contextual ID token.
Chris@17 262 */
Chris@17 263 protected function createContextualIdToken($id) {
Chris@17 264 return Crypt::hmacBase64($id, Settings::getHashSalt() . $this->container->get('private_key')->get());
Chris@17 265 }
Chris@17 266
Chris@17 267 }