diff --git a/core/modules/node/src/NodeGrantDatabaseStorage.php b/core/modules/node/src/NodeGrantDatabaseStorage.php index 6b6f280..0844891 100644 --- a/core/modules/node/src/NodeGrantDatabaseStorage.php +++ b/core/modules/node/src/NodeGrantDatabaseStorage.php @@ -190,7 +190,17 @@ public function alterQuery($query, array $tables, $op, AccountInterface $account // Now handle entities. $subquery->where("$nalias.$field = na.nid"); - $query->exists($subquery); + // If it's a left join to entity table and entity does not exist (i.e. + // entity_id is null) then skip access check. + if ($tableinfo['join type'] == 'LEFT' || $tableinfo['join type'] == 'LEFT OUTER') { + $subquery_cond = db_or(); + $subquery_cond->exists($subquery); + $subquery_cond->isNull("$nalias.$field"); + $query->condition($subquery_cond); + } + else { + $query->exists($subquery); + } } } } diff --git a/core/modules/node/src/Tests/NodeAccessJoinTest.php b/core/modules/node/src/Tests/NodeAccessJoinTest.php new file mode 100644 index 0000000..d1786f5 --- /dev/null +++ b/core/modules/node/src/Tests/NodeAccessJoinTest.php @@ -0,0 +1,179 @@ + 'related_article', + 'entity_type' => 'node', + 'translatable' => FALSE, + 'entity_types' => array(), + 'settings' => array( + 'target_type' => 'node', + ), + 'type' => 'entity_reference', + )); + $field_storage->save(); + $field = FieldConfig::create(array( + 'field_name' => 'related_article', + 'entity_type' => 'node', + 'bundle' => 'page', + 'label' => 'Related Article', + 'settings' => array( + 'handler' => 'default', + 'handler_settings' => array( + // Reference a single vocabulary. + 'target_bundles' => array( + 'article', + ), + ), + ), + )); + $field->save(); + + entity_get_display('node', 'page', 'default') + ->setComponent('related_article') + ->save(); + entity_get_form_display('node', 'page', 'default') + ->setComponent('related_article', array( + 'type' => 'entity_reference_autocomplete', + )) + ->save(); + + node_access_rebuild(); + \Drupal::state()->set('node_access_test.private', TRUE); + } + + function testNodeAccessJoin() { + + // User to add articles and test owner access. + $this->ownerUser = $this->drupalCreateUser(['access content', 'create article content']); + + foreach (array(0 => 'Public', 1 => 'Private') as $is_private => $type) { + $edit = array( + 'title' => t('@private_public Article', array('@private_public' => $type)), + 'type' => 'article', + 'uid' => $this->ownerUser->id(), + ); + if ($is_private) { + $edit['private'][0]['value'] = TRUE; + } + else { + $edit['private'][0]['value'] = FALSE; + } + + $node = $this->drupalCreateNode($edit); + $this->assertEqual($is_private, (int)$node->private->value, 'The private status of the node was properly set in the node_access_test table.' . $node->uid->target_id); + $this->articles[$is_private] = $node->id(); + } + + $edit = array( + 'type' => 'page', + 'title' => 'Page with no article', + ); + $node = $this->drupalCreateNode($edit); + foreach (array(0 => 'Public', 1 => 'Private') as $is_private => $type) { + $edit['title'] = t('Page with @private_public article', array('@private_public' => strtolower($type))); + $edit['related_article'][0]['target_id'] = $this->articles[$is_private]; + + $node = $this->drupalCreateNode($edit); + $this->assertEqual((int)$this->articles[$is_private], (int)$node->related_article->target_id, 'Proper article attached to page.'); + } + + ViewTestData::createTestViews(get_class($this), array('node_test_views')); + + $this->drupalLogin($this->ownerUser); + $this->drupalGet('test-node-access-join'); + $this->assertText('Page with no article', 'Node with null reference visible'); + $this->assertText('Page with private article', "Node with owner's private reference visible"); + $this->assertText('Page with public article', 'Node with public reference visible'); + + // User to check if entry with private reference is hidden. + $this->regularUser = $this->drupalCreateUser(['access content']); + $this->drupalLogin($this->regularUser); + $this->drupalGet('test-node-access-join'); + $this->assertText('Page with no article', 'Node with null reference visible'); + $this->assertNoText('Page with private article', "Node with private reference hidden"); + $this->assertText('Page with public article', 'Node with public reference visible'); + + // User to check if entry with private reference is visible. + $this->accessUser = $this->drupalCreateUser(['access content', 'node test view']); + $this->drupalLogin($this->accessUser); + $this->drupalGet('test-node-access-join'); + $this->assertText('Page with no article', 'Node with null reference visible'); + $this->assertText('Page with private article', "Node with private reference visible"); + $this->assertText('Page with public article', 'Node with public reference visible'); + + } + +} diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module index e195ea8..1338eac 100644 --- a/core/modules/node/tests/modules/node_access_test/node_access_test.module +++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module @@ -78,7 +78,7 @@ function node_access_test_node_grants($account, $op) { function node_access_test_node_access_records(NodeInterface $node) { $grants = array(); // For NodeAccessBaseTableTestCase, only set records for private nodes. - if (!\Drupal::state()->get('node_access_test.private') || $node->private->value) { + if (!\Drupal::state()->get('node_access_test.private') || (isset($node->private) && $node->private->value)) { // Groups 8888 and 8889 for the node_access_test realm both receive a view // grant for all controlled nodes. See node_access_test_node_grants(). $grants[] = array( diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml new file mode 100644 index 0000000..3027a5a --- /dev/null +++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml @@ -0,0 +1,264 @@ +langcode: en +status: true +dependencies: + config: + - node.type.page + module: + - node + - user +id: test_node_access_join +label: 'Test Node Access Join' +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: full + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: '‹ Previous' + next: 'Next ›' + first: '« First' + last: 'Last »' + quantity: 9 + style: + type: table + row: + type: fields + fields: + title: + id: title + table: node_field_data + field: title + entity_type: node + entity_field: title + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + settings: + link_to_entity: true + plugin_id: field + relationship: none + group_type: group + admin_label: '' + label: Title + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + type: string + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + title_1: + id: title_1 + table: node_field_data + field: title + relationship: related_article + group_type: group + admin_label: '' + label: Article + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: 'No article referenced' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: node + entity_field: title + plugin_id: field + filters: + status: + value: true + table: node_field_data + field: status + plugin_id: boolean + entity_type: node + entity_field: status + id: status + expose: + operator: '' + group: 1 + type: + id: type + table: node_field_data + field: type + value: + page: page + entity_type: node + entity_field: type + plugin_id: bundle + sorts: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + entity_type: node + entity_field: title + plugin_id: standard + title: 'Test Node Access Join' + header: { } + footer: { } + empty: { } + relationships: + related_article: + id: related_article + table: node__related_article + field: related_article + relationship: none + group_type: group + admin_label: 'related_article: Content' + required: false + plugin_id: standard + arguments: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + display_extenders: { } + path: test-node-access-join + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { }