"How to use Entity Field Queries" contatins the cryptic piece of information,

->addMetaData($key, $object)

Adds additional metadata to the query. One important usage of this method is to run the query as another user, since EntityFieldQuery's fieldCondition will take current user's permission into account[...]

and there is a DANGEROUS_ACCESS_CHECK_OPT_OUT query tag that bypasses access checks, implying that access checks are run by default when EFQs are run. But this issue suggests that access checks are NOT run, UNLESS one is using fields in EFQ, in which case the "field_sql_storage_field_storage_query() function will always add 'entity_field_access'".

So... what? Do EFQs respect node_access by default, or do you have to do something to invoke node_access?


EntityFieldQuery will always check the user has the permission to access the field, if "DANGEROUS_ACCESS_CHECK_OPT_OUT" is not between the tags added to the query, but that is not done directly. It is done by adding a tag to the query, as field_sql_storage_field_storage_query() does.

if (!isset($query->tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) {

It is then an implementation of hook_query_TAG_alter() that alters the query to check the permission. The Node module implement that hook with node_query_entity_field_access_alter().

function node_query_entity_field_access_alter(QueryAlterableInterface $query) {
  _node_query_node_access_alter($query, 'entity');

That is the reason why, before Drupal 7.15 (which introduced the "DANGEROUS_ACCESS_CHECK_OPT_OUT" tag), it was suggested to use $query->addMetaData('account', user_load(1)). The user #1 always have any permission, and checking the permission to access a field would be always positive.

_node_query_node_access_alter() contains the following code.

  // If $account can bypass node access, or there are no node access modules,
  // or the operation is 'view' and the $acount has a global view grant (i.e.,
  // a view grant for node ID 0), we don't need to alter the query.
  if (user_access('bypass node access', $account)) {
  if (!count(module_implements('node_grants'))) {
  if ($op == 'view' && node_access_view_all_nodes($account)) {

  // Omissis

  // Find all instances of the base table being joined -- could appear
  // more than once in the query, and could be aliased. Join each one to
  // the node_access table.

  $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
    // all of the node access conditions in a separate db_and() object and
    // then add it to the query at the end.
    $node_conditions = db_and();
  foreach ($tables as $nalias => $tableinfo) {
    $table = $tableinfo['table'];
    if (!($table instanceof SelectQueryInterface) && $table == $base_table) {
      // Set the subquery.
      $subquery = db_select('node_access', 'na')
       ->fields('na', array('nid'));

      $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) {
            ->condition('na.gid', $gid)
            ->condition('na.realm', $realm)

Definitively, the code checks the user has access to the node.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.