You are here

module_grants.node.inc in Module Grants 7

This file contains methods copied from node.module and modified to allow ANDing of grants, which is handled a function call to module_grants_apply_node_access_grants_condition

File

module_grants.node.inc
View source
<?php

/**
 * @file
 * This file contains methods copied from node.module and modified to allow
 * ANDing of grants, which is handled a function call to module_grants_apply_node_access_grants_condition
 */

/**
 * Copied from node_access(), with everything before module_invoke_all('node_access')
 * removed (since our override occurs during module_invoke_all('node_access')). Other changes include:
 * 1. Commented out the $rights caching code, we shouldn't need cache here since node_access() is already
 *   doing the caching
 * 2. Replace OR query of module grants with a call to module_grants_get_node_access_result()
 */
function _module_grants_node_access($op, $node, $account = NULL) {

  // We grant access to the node if both of the following conditions are met:
  // - No modules say to deny access.
  // - At least one module says to grant access.
  // If no module specified either allow or deny, we fall back to the
  // node_access table.

  //$access = module_invoke_all('node_access', $node, $op, $account);
  $access = module_grants_invoke_node_access($node, $op, $account);
  if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {

    //$rights[$account->uid][$cid][$op] = FALSE;
    return FALSE;
  }
  elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {

    //$rights[$account->uid][$cid][$op] = TRUE;
    return TRUE;
  }

  // Check if authors can view their own unpublished nodes.
  if ($op == 'view' && !$node->status && user_access('view own unpublished content', $account) && $account->uid == $node->uid && $account->uid != 0) {

    //$rights[$account->uid][$cid][$op] = TRUE;
    return TRUE;
  }

  // If the module did not override the access rights, use those set in the
  // node_access table.
  if ($op != 'create' && $node->nid) {
    if (module_implements('node_grants')) {
      if (module_grants_is_disabled()) {

        // if disabled, use the old logic from node.module
        $query = db_select('node_access');
        $query
          ->addExpression('1');
        $query
          ->condition('grant_' . $op, 1, '>=');
        $nids = db_or()
          ->condition('nid', $node->nid);
        if ($node->status) {
          $nids
            ->condition('nid', 0);
        }
        $query
          ->condition($nids);
        $query
          ->range(0, 1);
        $grants = db_or();
        foreach (node_access_grants($op, $account) as $realm => $gids) {
          foreach ($gids as $gid) {
            $grants
              ->condition(db_and()
              ->condition('gid', $gid)
              ->condition('realm', $realm));
          }
        }
        if (count($grants) > 0) {
          $query
            ->condition($grants);
        }
        $result = (bool) $query
          ->execute()
          ->fetchField();

        //$rights[$account->uid][$cid][$op] = $result;
      }
      else {
        $result = module_grants_get_node_access_result($node, $op, $account);
      }
      return $result;
    }
    elseif (is_object($node) && $op == 'view' && $node->status) {

      // If no modules implement hook_node_grants(), the default behavior is to
      // allow all users to view published nodes, so reflect that here.

      //$rights[$account->uid][$cid][$op] = TRUE;
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Copied from node_access_view_all_nodes(), changes include:
 * 1. Replace OR query of module grants with a call to module_grants_get_node_access_view_all_nodes_result()
 */
function _module_grants_node_access_view_all_nodes($account = NULL) {
  global $user;
  if (!$account) {
    $account = $user;
  }

  // Statically cache results in an array keyed by $account->uid.
  $access =& drupal_static(__FUNCTION__);
  if (isset($access[$account->uid])) {
    return $access[$account->uid];
  }

  // If no modules implement the node access system, access is always TRUE.
  if (!module_implements('node_grants')) {
    $access[$account->uid] = TRUE;
  }
  else {

    /*
    $query = db_select('node_access');
    $query->addExpression('COUNT(*)');
    $query
      ->condition('nid', 0)
      ->condition('grant_view', 1, '>=');

    $grants = db_or();
    foreach (node_access_grants('view', $account) as $realm => $gids) {
      foreach ($gids as $gid) {
        $grants->condition(db_and()
            ->condition('gid', $gid)
            ->condition('realm', $realm)
        );
      }
    }
    if (count($grants) > 0 ) {
      $query->condition($grants);
    }

    $access[$account->uid] = $query
      ->execute()
      ->fetchField();
    */
    $access[$account->uid] = module_grants_get_node_access_view_all_nodes_result($account);
  }
  return $access[$account->uid];
}

/**
 * Copied from _node_query_node_access_alter(), changes include:
 * 1. Replace calls to node_access_view_all_nodes() with _module_grants_node_access_view_all_nodes()
 * 2. Replace OR query of module grants with a call to module_grants_apply_subquery_for_node_query_node_access_alter()
 */
function _module_grants_node_query_node_access_alter($query, $type) {
  global $user;

  // Read meta-data from query, if provided.
  if (!($account = $query
    ->getMetaData('account'))) {
    $account = $user;
  }
  if (!($op = $query
    ->getMetaData('op'))) {
    $op = 'view';
  }

  // If $account can bypass node access, or there are no node access modules,
  // or the operation is 'view' and the $account has a global view grant
  // (such as a view grant for node ID 0), we don't need to alter the query.
  if (user_access('bypass node access', $account)) {
    return;
  }
  if (!count(module_implements('node_grants'))) {
    return;
  }

  /*
  if ($op == 'view' && node_access_view_all_nodes($account)) {
    return;
  }
  */
  if ($op == 'view' && _module_grants_node_access_view_all_nodes($account)) {
    return;
  }
  $tables = $query
    ->getTables();
  $base_table = $query
    ->getMetaData('base_table');

  // If no base table is specified explicitly, search for one.
  if (!$base_table) {
    $fallback = '';
    foreach ($tables as $alias => $table_info) {
      if (!$table_info instanceof SelectQueryInterface) {
        $table = $table_info['table'];

        // If the node table is in the query, it wins immediately.
        if ($table == 'node') {
          $base_table = $table;
          break;
        }

        // Check whether the table has a foreign key to node.nid. If it does,
        // do not run this check again as we found a base table and only node
        // can triumph that.
        if (!$base_table) {

          // The schema is cached.
          $schema = drupal_get_schema($table);
          if (isset($schema['fields']['nid'])) {
            if (isset($schema['foreign keys'])) {
              foreach ($schema['foreign keys'] as $relation) {
                if ($relation['table'] === 'node' && $relation['columns'] === array(
                  'nid' => 'nid',
                )) {
                  $base_table = $table;
                }
              }
            }
            else {

              // At least it's a nid. A table with a field called nid is very
              // very likely to be a node.nid in a node access query.
              $fallback = $table;
            }
          }
        }
      }
    }

    // If there is nothing else, use the fallback.
    if (!$base_table) {
      if ($fallback) {
        watchdog('security', 'Your node listing query is using @fallback as a base table in a query tagged for node access. This might not be secure and might not even work. Specify foreign keys in your schema to node.nid ', array(
          '@fallback' => $fallback,
        ), WATCHDOG_WARNING);
        $base_table = $fallback;
      }
      else {
        throw new Exception(t('Query tagged for node access but there is no nid. Add foreign keys to node.nid in schema to fix.'));
      }
    }
  }

  // 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) {
          $grant_conditions->condition(db_and()
              ->condition('na.gid', $gid)
              ->condition('na.realm', $realm)
          );
        }
      }

      // Attach conditions to the subquery for nodes.
      if (count($grant_conditions->conditions())) {
        $subquery->condition($grant_conditions);
      }
      $subquery->condition('na.grant_' . $op, 1, '>=');
      $field = 'nid';
      // Now handle entities.
      if ($type == 'entity') {
        // Set a common alias for entities.
        $base_alias = $nalias;
        $field = 'entity_id';
      }
      $subquery->where("$nalias.$field = na.nid");

      // For an entity query, attach the subquery to entity conditions.
      if ($type == 'entity') {
        $node_conditions->exists($subquery);
      }
      // Otherwise attach it to the node query itself.
      else {
        $query->exists($subquery);
      }
    }
  }

  if ($type == 'entity' && count($subquery->conditions())) {
    // All the node access conditions are only for field values belonging to
    // nodes.
    $node_conditions->condition("$base_alias.entity_type", 'node');
    $or = db_or();
    $or->condition($node_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.entity_type", 'node', '<>');
    // Add the compiled set of rules to the query.
    $query->condition($or);
  }
  */
  module_grants_apply_subquery_for_node_query_node_access_alter($query, $type, $base_table, $op, $account);
}

Functions

Namesort descending Description
_module_grants_node_access Copied from node_access(), with everything before module_invoke_all('node_access') removed (since our override occurs during module_invoke_all('node_access')). Other changes include: 1. Commented out the $rights caching code, we…
_module_grants_node_access_view_all_nodes Copied from node_access_view_all_nodes(), changes include: 1. Replace OR query of module grants with a call to module_grants_get_node_access_view_all_nodes_result()
_module_grants_node_query_node_access_alter Copied from _node_query_node_access_alter(), changes include: 1. Replace calls to node_access_view_all_nodes() with _module_grants_node_access_view_all_nodes() 2. Replace OR query of module grants with a call to…