3 namespace Drupal\Tests\node\Functional;
5 use Drupal\Core\Language\LanguageInterface;
6 use Drupal\field\Entity\FieldConfig;
7 use Drupal\language\Entity\ConfigurableLanguage;
8 use Drupal\node\Entity\NodeType;
9 use Drupal\user\Entity\User;
10 use Drupal\field\Entity\FieldStorageConfig;
13 * Tests node access functionality with multiple languages and two node access
18 class NodeAccessLanguageAwareCombinationTest extends NodeTestBase {
21 * Enable language and two node access modules.
25 public static $modules = ['language', 'node_access_test_language', 'node_access_test'];
28 * A set of nodes to use in testing.
30 * @var \Drupal\node\NodeInterface[]
32 protected $nodes = [];
35 * A normal authenticated user.
37 * @var \Drupal\user\UserInterface.
44 * @var \Drupal\user\UserInterface.
48 protected function setUp() {
51 node_access_test_add_field(NodeType::load('page'));
53 // Create the 'private' field, which allows the node to be marked as private
54 // (restricted access) in a given translation.
55 $field_storage = FieldStorageConfig::create([
56 'field_name' => 'field_private',
57 'entity_type' => 'node',
61 $field_storage->save();
64 'field_storage' => $field_storage,
67 'type' => 'options_buttons',
70 'on_label' => 'Private',
71 'off_label' => 'Not private',
75 // After enabling a node access module, the access table has to be rebuild.
76 node_access_rebuild();
78 // Add Hungarian and Catalan.
79 ConfigurableLanguage::createFromLangcode('hu')->save();
80 ConfigurableLanguage::createFromLangcode('ca')->save();
82 // Create a normal authenticated user.
83 $this->webUser = $this->drupalCreateUser(['access content']);
85 // Load the user 1 user for later use as an admin user with permission to
87 $this->adminUser = User::load(1);
89 // The node_access_test_language module allows individual translations of a
90 // node to be marked private (not viewable by normal users), and the
91 // node_access_test module allows whole nodes to be marked private. (In a
92 // real-world implementation, hook_node_access_records_alter() might be
93 // implemented by one or both modules to enforce that private nodes or
94 // translations are always private, but we want to test the default,
95 // additive behavior of node access).
97 // Create six Hungarian nodes with Catalan translations:
98 // 1. One public with neither language marked as private.
99 // 2. One private with neither language marked as private.
100 // 3. One public with only the Hungarian translation private.
101 // 4. One public with only the Catalan translation private.
102 // 5. One public with both the Hungarian and Catalan translations private.
103 // 6. One private with both the Hungarian and Catalan translations private.
104 $this->nodes['public_both_public'] = $node = $this->drupalCreateNode([
107 'field_private' => [['value' => 0]],
110 $translation = $node->addTranslation('ca');
111 $translation->title->value = $this->randomString();
112 $translation->field_private->value = 0;
115 $this->nodes['private_both_public'] = $node = $this->drupalCreateNode([
118 'field_private' => [['value' => 0]],
121 $translation = $node->addTranslation('ca');
122 $translation->title->value = $this->randomString();
123 $translation->field_private->value = 0;
126 $this->nodes['public_hu_private'] = $node = $this->drupalCreateNode([
129 'field_private' => [['value' => 1]],
132 $translation = $node->addTranslation('ca');
133 $translation->title->value = $this->randomString();
134 $translation->field_private->value = 0;
137 $this->nodes['public_ca_private'] = $node = $this->drupalCreateNode([
140 'field_private' => [['value' => 0]],
143 $translation = $node->addTranslation('ca');
144 $translation->title->value = $this->randomString();
145 $translation->field_private->value = 1;
148 $this->nodes['public_both_private'] = $node = $this->drupalCreateNode([
151 'field_private' => [['value' => 1]],
154 $translation = $node->addTranslation('ca');
155 $translation->title->value = $this->randomString();
156 $translation->field_private->value = 1;
159 $this->nodes['private_both_private'] = $node = $this->drupalCreateNode([
162 'field_private' => [['value' => 1]],
165 $translation = $node->addTranslation('ca');
166 $translation->title->value = $this->randomString();
167 $translation->field_private->value = 1;
170 $this->nodes['public_no_language_private'] = $this->drupalCreateNode([
171 'field_private' => [['value' => 1]],
173 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
175 $this->nodes['public_no_language_public'] = $this->drupalCreateNode([
176 'field_private' => [['value' => 0]],
178 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
180 $this->nodes['private_no_language_private'] = $this->drupalCreateNode([
181 'field_private' => [['value' => 1]],
183 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
185 $this->nodes['private_no_language_public'] = $this->drupalCreateNode([
186 'field_private' => [['value' => 1]],
188 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
193 * Tests node access and node access queries with multiple node languages.
195 public function testNodeAccessLanguageAwareCombination() {
197 $expected_node_access = ['view' => TRUE, 'update' => FALSE, 'delete' => FALSE];
198 $expected_node_access_no_access = ['view' => FALSE, 'update' => FALSE, 'delete' => FALSE];
200 // When the node and both translations are public, access should always be
202 $this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public'], $this->webUser);
203 $this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public']->getTranslation('hu'), $this->webUser);
204 $this->assertNodeAccess($expected_node_access, $this->nodes['public_both_public']->getTranslation('ca'), $this->webUser);
206 // If the node is marked private but both existing translations are not,
207 // access should still be granted, because the grants are additive.
208 $this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public'], $this->webUser);
209 $this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public']->getTranslation('hu'), $this->webUser);
210 $this->assertNodeAccess($expected_node_access, $this->nodes['private_both_public']->getTranslation('ca'), $this->webUser);
212 // If the node is marked private, but a existing translation is public,
213 // access should only be granted for the public translation. With the
214 // Hungarian translation marked as private, but the Catalan translation
215 // public, the access is granted.
216 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_hu_private'], $this->webUser);
217 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_hu_private']->getTranslation('hu'), $this->webUser);
218 $this->assertNodeAccess($expected_node_access, $this->nodes['public_hu_private']->getTranslation('ca'), $this->webUser);
220 // With the Catalan translation marked as private, but the node public,
221 // access is granted for the existing Hungarian translation, but not for the
223 $this->assertNodeAccess($expected_node_access, $this->nodes['public_ca_private'], $this->webUser);
224 $this->assertNodeAccess($expected_node_access, $this->nodes['public_ca_private']->getTranslation('hu'), $this->webUser);
225 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_ca_private']->getTranslation('ca'), $this->webUser);
227 // With both translations marked as private, but the node public, access
228 // should be denied in all cases.
229 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private'], $this->webUser);
230 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private']->getTranslation('hu'), $this->webUser);
231 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_both_private']->getTranslation('ca'), $this->webUser);
233 // If the node and both its existing translations are private, access should
234 // be denied in all cases.
235 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private'], $this->webUser);
236 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private']->getTranslation('hu'), $this->webUser);
237 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_both_private']->getTranslation('ca'), $this->webUser);
239 // No access for all languages as the language aware node access module
241 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['public_no_language_private'], $this->webUser);
243 // Access only for request with no language defined.
244 $this->assertNodeAccess($expected_node_access, $this->nodes['public_no_language_public'], $this->webUser);
246 // No access for all languages as both node access modules deny access.
247 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_no_language_private'], $this->webUser);
249 // No access for all languages as the non language aware node access module
251 $this->assertNodeAccess($expected_node_access_no_access, $this->nodes['private_no_language_public'], $this->webUser);
253 // Query the node table with the node access tag in several languages.
255 // Query with no language specified. The fallback (hu or und) will be used.
256 $select = db_select('node', 'n')
257 ->fields('n', ['nid'])
258 ->addMetaData('account', $this->webUser)
259 ->addTag('node_access');
260 $nids = $select->execute()->fetchAllAssoc('nid');
262 // Four nodes should be returned with public Hungarian translations or the
263 // no language public node.
264 $this->assertEqual(count($nids), 4, 'db_select() returns 4 nodes when no langcode is specified.');
265 $this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is full public node.');
266 $this->assertTrue(array_key_exists($this->nodes['public_ca_private']->id(), $nids), 'Returned node ID is Hungarian public only node.');
267 $this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
268 $this->assertTrue(array_key_exists($this->nodes['public_no_language_public']->id(), $nids), 'Returned node ID is no language public node.');
270 // Query with Hungarian (hu) specified.
271 $select = db_select('node', 'n')
272 ->fields('n', ['nid'])
273 ->addMetaData('account', $this->webUser)
274 ->addMetaData('langcode', 'hu')
275 ->addTag('node_access');
276 $nids = $select->execute()->fetchAllAssoc('nid');
278 // Three nodes should be returned (with public Hungarian translations).
279 $this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes.');
280 $this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is both public node.');
281 $this->assertTrue(array_key_exists($this->nodes['public_ca_private']->id(), $nids), 'Returned node ID is Hungarian public only node.');
282 $this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
284 // Query with Catalan (ca) specified.
285 $select = db_select('node', 'n')
286 ->fields('n', ['nid'])
287 ->addMetaData('account', $this->webUser)
288 ->addMetaData('langcode', 'ca')
289 ->addTag('node_access');
290 $nids = $select->execute()->fetchAllAssoc('nid');
292 // Three nodes should be returned (with public Catalan translations).
293 $this->assertEqual(count($nids), 3, 'db_select() returns 3 nodes.');
294 $this->assertTrue(array_key_exists($this->nodes['public_both_public']->id(), $nids), 'Returned node ID is both public node.');
295 $this->assertTrue(array_key_exists($this->nodes['public_hu_private']->id(), $nids), 'Returned node ID is Catalan public only node.');
296 $this->assertTrue(array_key_exists($this->nodes['private_both_public']->id(), $nids), 'Returned node ID is both public non-language-aware private only node.');
298 // Query with German (de) specified.
299 $select = db_select('node', 'n')
300 ->fields('n', ['nid'])
301 ->addMetaData('account', $this->webUser)
302 ->addMetaData('langcode', 'de')
303 ->addTag('node_access');
304 $nids = $select->execute()->fetchAllAssoc('nid');
306 // There are no nodes with German translations, so no results are returned.
307 $this->assertTrue(empty($nids), 'db_select() returns an empty result.');
309 // Query the nodes table as admin user (full access) with the node access
310 // tag and no specific langcode.
311 $select = db_select('node', 'n')
312 ->fields('n', ['nid'])
313 ->addMetaData('account', $this->adminUser)
314 ->addTag('node_access');
315 $nids = $select->execute()->fetchAllAssoc('nid');
317 // All nodes are returned.
318 $this->assertEqual(count($nids), 10, 'db_select() returns all nodes.');
320 // Query the nodes table as admin user (full access) with the node access
321 // tag and langcode de.
322 $select = db_select('node', 'n')
323 ->fields('n', ['nid'])
324 ->addMetaData('account', $this->adminUser)
325 ->addMetaData('langcode', 'de')
326 ->addTag('node_access');
327 $nids = $select->execute()->fetchAllAssoc('nid');
329 // Even though there is no German translation, all nodes are returned
330 // because node access filtering does not occur when the user is user 1.
331 $this->assertEqual(count($nids), 10, 'db_select() returns all nodes.');