/** * See _node_access_where_sql() for a non-views query based implementation. */ public function query() { $account = $this->view->getUser(); if (!$account->hasPermission('administer nodes')) { $table = $this->ensureMyTable(); $grants = db_or(); foreach (node_access_grants('view', $account) as $realm => $gids) { foreach ($gids as $gid) { $grants->condition(db_and()->condition($table . '.gid', $gid)->condition($table . '.realm', $realm)); } } $this->query->addWhere('AND', $grants); $this->query->addWhere('AND', $table . '.grant_view', 1, '>='); } }
/** * Checks the node grants for the given operation. * * @param string $operation * The operation to check the node grants for. * * @return string * The string representation of the cache context. */ protected function checkNodeGrants($operation) { // When checking the grants for the 'view' operation and the current user // has a global view grant (i.e. a view grant for node ID 0) — note that // this is automatically the case if no node access modules exist (no // hook_node_grants() implementations) then we don't need to determine the // exact node view grants for the current user. if ($operation === 'view' && node_access_view_all_nodes($this->user)) { return 'view.all'; } $grants = node_access_grants($operation, $this->user); $grants_context_parts = []; foreach ($grants as $realm => $gids) { $grants_context_parts[] = $realm . ':' . implode(',', $gids); } return $operation . '.' . implode(';', $grants_context_parts); }
/** * {@inheritdoc} */ public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table) { if (!($langcode = $query->getMetaData('langcode'))) { $langcode = FALSE; } // 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); foreach ($tables as $nalias => $tableinfo) { $table = $tableinfo['table']; if (!$table instanceof SelectInterface && $table == $base_table) { // Set the subquery. $subquery = $this->database->select('node_access', 'na')->fields('na', array('nid')); // If any grant exists for the specified user, then user has access to the // node for the specified operation. $grant_conditions = static::buildGrantsQueryCondition($grants); // Attach conditions to the subquery for nodes. if (count($grant_conditions->conditions())) { $subquery->condition($grant_conditions); } $subquery->condition('na.grant_' . $op, 1, '>='); // Add langcode-based filtering if this is a multilingual site. if (\Drupal::languageManager()->isMultilingual()) { // If no specific langcode to check for is given, use the grant entry // which is set as a fallback. // If a specific langcode is given, use the grant entry for it. if ($langcode === FALSE) { $subquery->condition('na.fallback', 1, '='); } else { $subquery->condition('na.langcode', $langcode, '='); } } $field = 'nid'; // Now handle entities. $subquery->where("{$nalias}.{$field} = na.nid"); $query->exists($subquery); } } }
/** * Perform alterations to a structured query for a given tag. * * @param $query * An Query object describing the composite parts of a SQL query. * * @see hook_query_alter() * @see node_query_node_access_alter() * @see QueryAlterableInterface * @see SelectQueryInterface */ function hook_query_TAG_alter(QueryAlterableInterface $query) { // Skip the extra expensive alterations if site has no node access control modules. if (!node_access_view_all_nodes()) { // Prevent duplicates records. $query->distinct(); // The recognized operations are 'view', 'update', 'delete'. if (!($op = $query->getMetaData('op'))) { $op = 'view'; } // Skip the extra joins and conditions for node admins. if (!user_access('bypass node access')) { // The node_access table has the access grants for any given node. $access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid'); $or = db_or(); // If any grant exists for the specified user, then user has access to the node for the specified operation. foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) { foreach ($gids as $gid) { $or->condition(db_and()->condition($access_alias . '.gid', $gid)->condition($access_alias . '.realm', $realm)); } } if (count($or->conditions())) { $query->condition($or); } $query->condition($access_alias . 'grant_' . $op, 1, '>='); } } }
/** * Adds a node access filter to a search query, if applicable. * * @param \Drupal\search_api\Query\QueryInterface $query * The query to which a node access filter should be added, if applicable. * @param \Drupal\Core\Session\AccountInterface $account * The user for whom the search is executed. * * @throws \Drupal\search_api\SearchApiException * Thrown if not all necessary fields are indexed on the index. */ protected function addNodeAccess(QueryInterface $query, AccountInterface $account) { // Don't do anything if the user can access all content. if ($account->hasPermission('bypass node access')) { return; } // Gather the affected datasources, grouped by entity type, as well as the // unaffected ones. $affected_datasources = array(); $unaffected_datasources = array(); foreach ($this->index->getDatasources() as $datasource_id => $datasource) { $entity_type = $datasource->getEntityTypeId(); if (in_array($entity_type, array('node', 'comment'))) { $affected_datasources[$entity_type][] = $datasource_id; } else { $unaffected_datasources[] = $datasource_id; } } // The filter structure we want looks like this: // [belongs to other datasource] // OR // ( // [is enabled (or was created by the user, if applicable)] // AND // [grants view access to one of the user's gid/realm combinations] // ) // If there are no "other" datasources, we don't need the nested OR, // however, and can add the "ADD" // @todo Add a filter tag, once they are implemented. if ($unaffected_datasources) { $outer_conditions = $query->createConditionGroup('OR', array('content_access')); $query->addConditionGroup($outer_conditions); foreach ($unaffected_datasources as $datasource_id) { $outer_conditions->addCondition('search_api_datasource', $datasource_id); } $access_conditions = $query->createConditionGroup('AND'); $outer_conditions->addConditionGroup($access_conditions); } else { $access_conditions = $query; } if (!$account->hasPermission('access content')) { unset($affected_datasources['node']); } if (!$account->hasPermission('access comments')) { unset($affected_datasources['comment']); } // If the user does not have the permission to see any content at all, deny // access to all items from affected datasources. if (!$affected_datasources) { // If there were "other" datasources, the existing filter will already // remove all results of node or comment datasources. Otherwise, we should // not return any results at all. if (!$unaffected_datasources) { // @todo More elegant way to return no results? // @todo Now that field IDs can be picked freely, this can theoretically // even fail! Needs to be fixed! $query->addCondition('search_api_language', ''); } return; } // Collect all the required fields that need to be part of the index. $unpublished_own = $account->hasPermission('view own unpublished content'); $enabled_conditions = $query->createConditionGroup('OR', array('content_access_enabled')); foreach ($affected_datasources as $entity_type => $datasources) { foreach ($datasources as $datasource_id) { // If this is a comment datasource, or users cannot view their own // unpublished nodes, a simple filter on "status" is enough. Otherwise, // it's a bit more complicated. $status_field = $this->findField($datasource_id, 'status', 'boolean'); if ($status_field) { $enabled_conditions->addCondition($status_field->getFieldIdentifier(), TRUE); } if ($entity_type == 'node' && $unpublished_own) { $author_field = $this->findField($datasource_id, 'uid', 'integer'); if ($author_field) { $enabled_conditions->addCondition($author_field->getFieldIdentifier(), $account->id()); } } } } $access_conditions->addConditionGroup($enabled_conditions); // Filter by the user's node access grants. $node_grants_field = $this->findField(NULL, 'search_api_node_grants', 'string'); if (!$node_grants_field) { return; } $node_grants_field_id = $node_grants_field->getFieldIdentifier(); $grants_conditions = $query->createConditionGroup('OR', array('content_access_grants')); $grants = node_access_grants('view', $account); foreach ($grants as $realm => $gids) { foreach ($gids as $gid) { $grants_conditions->addCondition($node_grants_field_id, "node_access_{$realm}:{$gid}"); } } // Also add items that are accessible for everyone by checking the "access // all" pseudo grant. $grants_conditions->addCondition($node_grants_field_id, 'node_access__all'); $access_conditions->addConditionGroup($grants_conditions); }