summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2010-08-22 16:11:12 +0000
committerDries Buytaert <dries@buytaert.net>2010-08-22 16:11:12 +0000
commit479b71232be6634e59868d72471d798c4fbabfce (patch)
tree5c5e336e374acf9792b7da6c390d75cc43c71439 /modules
parent1651cf34bfbdf02f6c03f8d00b025a610d24df23 (diff)
downloadbrdo-479b71232be6634e59868d72471d798c4fbabfce.tar.gz
brdo-479b71232be6634e59868d72471d798c4fbabfce.tar.bz2
- Patch #860180 by chx, dixon_, jhodgdon: entity listing and loading does not allow for node access.
Diffstat (limited to 'modules')
-rw-r--r--modules/field/modules/field_sql_storage/field_sql_storage.module3
-rw-r--r--modules/node/node.module97
-rw-r--r--modules/node/node.test70
-rw-r--r--modules/node/tests/node_access_test.module40
4 files changed, 197 insertions, 13 deletions
diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.module b/modules/field/modules/field_sql_storage/field_sql_storage.module
index 65e6bdf38..407e9186f 100644
--- a/modules/field/modules/field_sql_storage/field_sql_storage.module
+++ b/modules/field/modules/field_sql_storage/field_sql_storage.module
@@ -502,6 +502,8 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
}
else {
$select_query = db_select($tablename, $table_alias);
+ $select_query->addTag('entity_field_access');
+ $select_query->addMetaData('base_table', $tablename);
$select_query->fields($table_alias, array('entity_id', 'revision_id', 'bundle'));
// As only a numeric ID is stored instead of the entity type add the
// field_config_entity_type table to resolve the etid to a more readable
@@ -547,6 +549,7 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
if (isset($query->deleted)) {
$select_query->condition("$field_base_table.deleted", (int) $query->deleted);
}
+
if ($query->propertyConditions || $query->propertyOrder) {
if (empty($query->entityConditions['entity_type']['value'])) {
throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
diff --git a/modules/node/node.module b/modules/node/node.module
index 1b646b43e..ca42c5b3e 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -2767,7 +2767,6 @@ function node_access($op, $node, $account = NULL) {
$rights[$account->uid][$cid][$op] = TRUE;
return TRUE;
}
-
if (!user_access('access content', $account)) {
$rights[$account->uid][$cid][$op] = FALSE;
return FALSE;
@@ -3009,6 +3008,33 @@ function node_access_view_all_nodes() {
* 'update' and 'delete').
*/
function node_query_node_access_alter(QueryAlterableInterface $query) {
+ _node_query_node_access_alter($query, 'node', 'node');
+}
+
+/**
+ * Implements hook_query_TAG_alter().
+ *
+ * This function implements the same functionality as
+ * node_query_node_access_alter() for the SQL field storage engine. Node access
+ * conditions are added for field values belonging to nodes only.
+ */
+function node_query_entity_field_access_alter(QueryAlterableInterface $query) {
+ _node_query_node_access_alter($query, $query->getMetaData('base_table'), 'entity');
+}
+
+/**
+ * Helper for node access functions.
+ *
+ * @param $query
+ * The query to add conditions to.
+ * @param $base_table
+ * The table holding node ids.
+ * @param $type
+ * Either 'node' or 'entity' depending on what sort of query it is. See
+ * node_query_node_access_alter() and node_query_entity_field_access_alter()
+ * for more.
+ */
+function _node_query_node_access_alter($query, $base_table, $type) {
global $user;
// Read meta-data from query, if provided.
@@ -3037,31 +3063,82 @@ function node_query_node_access_alter(QueryAlterableInterface $query) {
$tables = $query->getTables();
$grants = node_access_grants($op, $account);
+ if ($type == 'entity') {
+ // The original query looked something like:
+ // @code
+ // SELECT nid FROM sometable s
+ // INNER JOIN node_access na ON na.nid = s.nid
+ // WHERE ($node_access_conditions)
+ // @endcode
+ //
+ // Our query will look like:
+ // @code
+ // SELECT entity_type, entity_id
+ // FROM field_data_something s
+ // LEFT JOIN node_access na ON s.entity_id = na.nid
+ // WHERE (entity_type = 'node' AND $node_access_conditions) OR (entity_type <> 'node')
+ // @endcode
+ //
+ // So instead of directly adding to the query object, we need to collect
+ // in a separate db_and() object and then at the end add it to the query.
+ $entity_conditions = db_and();
+ }
foreach ($tables as $nalias => $tableinfo) {
$table = $tableinfo['table'];
- if (!($table instanceof SelectQueryInterface) && $table == 'node') {
+ if (!($table instanceof SelectQueryInterface) && $table == $base_table) {
- // The node_access table has the access grants for any given node.
- $access_alias = $query->join('node_access', 'na', '%alias.nid = ' . $nalias . '.nid');
- $or = db_or();
+ // The node_access table has the access grants for any given node so JOIN
+ // it to the table containing the nid which can be either the node
+ // table or a field value table.
+ if ($type == 'node') {
+ $access_alias = $query->join('node_access', 'na', '%alias.nid = ' . $nalias . '.nid');
+ }
+ else {
+ $access_alias = $query->leftJoin('node_access', 'na', '%alias.nid = ' . $nalias . '.entity_id');
+ $base_alias = $nalias;
+ }
+
+ $grant_conditions = db_or();
// If any grant exists for the specified user, then user has access
// to the node for the specified operation.
foreach ($grants as $realm => $gids) {
foreach ($gids as $gid) {
- $or->condition(db_and()
+ $grant_conditions->condition(db_and()
->condition($access_alias . '.gid', $gid)
->condition($access_alias . '.realm', $realm)
);
}
}
- if (count($or->conditions())) {
- $query->condition($or);
+ $count = count($grant_conditions->conditions());
+ if ($type == 'node') {
+ if ($count) {
+ $query->condition($grant_conditions);
+ }
+ $query->condition($access_alias . '.grant_' . $op, 1, '>=');
+ }
+ else {
+ if ($count) {
+ $entity_conditions->condition($grant_conditions);
+ }
+ $entity_conditions->condition($access_alias . '.grant_' . $op, 1, '>=');
}
-
- $query->condition($access_alias . '.grant_' . $op, 1, '>=');
}
}
+
+ if ($type == 'entity' && count($entity_conditions->conditions())) {
+ // All the node access conditions are only for field values belonging to
+ // nodes.
+ $etid = variable_get('field_sql_storage_node_etid');
+ $entity_conditions->condition("$base_alias.etid", $etid);
+ $or = db_or();
+ $or->condition($entity_conditions);
+ // If the field value belongs to a non-node entity type then this function
+ // does not do anything with it.
+ $or->condition("$base_alias.etid", $etid, '<>');
+ // Add the compiled set of rules to the query.
+ $query->condition($or);
+ }
}
/**
diff --git a/modules/node/node.test b/modules/node/node.test
index 028275b3e..69dded38b 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -1596,7 +1596,8 @@ class NodeQueryAlter extends DrupalWebTestCase {
$this->drupalCreateNode();
$this->drupalCreateNode();
- // Create user with simple node access permission.
+ // Create user with simple node access permission. The 'node test view'
+ // permission is implemented and granted by the node_access_test module.
$this->accessUser = $this->drupalCreateUser(array('access content', 'node test view'));
$this->noAccessUser = $this->drupalCreateUser(array('access content'));
}
@@ -1606,14 +1607,12 @@ class NodeQueryAlter extends DrupalWebTestCase {
*/
function testNodeQueryAlterWithUI() {
// Verify that a user with access permission can see at least one node.
-
$this->drupalLogin($this->accessUser);
$this->drupalGet('node_access_test_page');
$this->assertText('Yes, 4 nodes', "4 nodes were found for access user");
$this->assertNoText('Exception', "No database exception");
// Verify that a user with no access permission cannot see nodes.
-
$this->drupalLogin($this->noAccessUser);
$this->drupalGet('node_access_test_page');
$this->assertText('No nodes', "No nodes were found for no access user");
@@ -1692,6 +1691,71 @@ class NodeQueryAlter extends DrupalWebTestCase {
}
}
+
+/**
+ * Tests node_query_entity_field_access_alter().
+ */
+class NodeEntityFieldQueryAlter extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Node entity query alter',
+ 'description' => 'Test that node access entity queries are properly altered by the node module.',
+ 'group' => 'Node',
+ );
+ }
+
+ /**
+ * User with permission to view content.
+ */
+ protected $accessUser;
+
+ /**
+ * User without permission to view content.
+ */
+ protected $noAccessUser;
+
+ function setUp() {
+ parent::setUp('node_access_test');
+ node_access_rebuild();
+
+ // Creating 4 nodes with an entity field so we can test that sort of query
+ // alter. All field values starts with 'A' so we can identify and fetch them
+ // in the node_access_test module.
+ $settings = array('language' => LANGUAGE_NONE);
+ for ($i = 0; $i < 4; $i++) {
+ $body = array(
+ 'value' => 'A' . $this->randomName(32),
+ 'format' => filter_default_format(),
+ );
+ $settings['body'][LANGUAGE_NONE][0] = $body;
+ $this->drupalCreateNode($settings);
+ }
+
+ // Create user with simple node access permission. The 'node test view'
+ // permission is implemented and granted by the node_access_test module.
+ $this->accessUser = $this->drupalCreateUser(array('access content', 'node test view'));
+ $this->noAccessUser = $this->drupalCreateUser(array('access content'));
+ }
+
+ /**
+ * Tests that node access permissions are followed.
+ */
+ function testNodeQueryAlterWithUI() {
+ // Verify that a user with access permission can see at least one node.
+ $this->drupalLogin($this->accessUser);
+ $this->drupalGet('node_access_entity_test_page');
+ $this->assertText('Yes, 4 nodes', "4 nodes were found for access user");
+ $this->assertNoText('Exception', "No database exception");
+
+ // Verify that a user with no access permission cannot see nodes.
+ $this->drupalLogin($this->noAccessUser);
+ $this->drupalGet('node_access_entity_test_page');
+ $this->assertText('No nodes', "No nodes were found for no access user");
+ $this->assertNoText('Exception', "No database exception");
+ }
+}
+
/**
* Test node token replacement in strings.
*/
diff --git a/modules/node/tests/node_access_test.module b/modules/node/tests/node_access_test.module
index eaae6b80a..ac71667ef 100644
--- a/modules/node/tests/node_access_test.module
+++ b/modules/node/tests/node_access_test.module
@@ -58,6 +58,12 @@ function node_access_test_menu() {
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
+ $items['node_access_entity_test_page'] = array(
+ 'title' => 'Node access test',
+ 'page callback' => 'node_access_entity_test_page',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_SUGGESTED_ITEM,
+ );
return $items;
}
@@ -100,3 +106,37 @@ function node_access_test_page() {
return $output;
}
+
+/**
+ * Page callback for node access entity test page.
+ *
+ * Page should say "No nodes" if there are no nodes, and "Yes, # nodes" (with
+ * the number filled in) if there were nodes the user could access. Also, the
+ * database query is shown, and a list of the node IDs, for debugging purposes.
+ * And if there is a query exception, the page says "Exception" and gives the
+ * error.
+ */
+function node_access_entity_test_page() {
+ $output = '';
+ try {
+ $query = new EntityFieldQuery;
+ $result = $query->fieldCondition('body', 'value', 'A', 'STARTS_WITH')->execute();
+ if (!empty($result['node'])) {
+ $output .= '<p>Yes, ' . count($result['node']) . ' nodes</p>';
+ $output .= '<ul>';
+ foreach ($result['node'] as $nid => $v) {
+ $output .= '<li>' . $nid . '</li>';
+ }
+ $output .= '</ul>';
+ }
+ else {
+ $output .= '<p>No nodes</p>';
+ }
+ }
+ catch (Exception $e) {
+ $output = '<p>Exception</p>';
+ $output .= '<p>' . $e->getMessage() . '</p>';
+ }
+
+ return $output;
+}