Hi there,

I have some content (personal information) where users can specify whether the content is public or private. I am using a taxonomy reference for that, and I added in my module an implementation of hook_node_access() to specify who can see the content and in what conditions (I am also using og, and the conditions are related to having a specific role in a group).

When my users access the node individually, the hook_node_access() is being called, and access to the content is effectively being granted/denied as necessary.

But, when I use view to list all these content, the user (regardless of who he is) has access to the whole information (title, body). Only when he click on the title (and try to access the node directly) does he get the access denied.

It seems that for performance reasons, hook_node_access is not being called when doing node listing (and I suspect that it applies to view as well).

from http://api.drupal.org/api/drupal/modules--node--node.module/group/node_a...

In node listings, the process above is followed except that hook_node_access() is not called on each node for performance reasons and for proper functioning of the pager system. When adding a node listing to your module, be sure to use a dynamic query created by db_select() and add a tag of "node_access". This will allow modules dealing with node access to ensure only nodes to which the user has access are retrieved, through the use of hook_query_TAG_alter().

In views, there is a filter called 'Content access: access' which should not be needed for node (according to the help provided by the filter), but it does not seem to have any effect.

Any suggestion on preventing view from listing content that the user should not be able to access?

Comments

dawehner’s picture

Category: bug » support
Status: Active » Fixed
Issue tags: -views

There are two ways to do it:

The first one is definitive the preferring way to do it.

Update the category to support request, because this is definitive not a bug of views. It's a fundamental design of drupal listings.

Before adding tags read the issue tag guidelines. Do NOT use tags for adding random keywords or duplicating any other fields. Separate terms with a comma, not a space.

spouilly’s picture

Thanks dereine, I will look into it.

I apologize for the mistakes in the "issue reporting", I have been careless with the tags.

Cheers,

Sylvain

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

zilverdistel’s picture

Thanks!
I used the first approach with hook_node_grants() and hook_node_access_records(), in combination with the "content access" filter in views.

johnv’s picture

Status: Closed (fixed) » Active

I am sorry to open this again.

I have the following hooks in place, and they are working fine when viewing an node page, but none of them are called when creating a view of nodes: hook_node_grants(), hook_node_access(), hook_node_access_records().

I have set 'Disable SQL rewriting' at some views, but that only seem to disable permissions, not node_access.
I have added 'Content access: access' (although you shouldn't need to) on the view, but it does not make any difference.
I made sure that hte permission 'Bypass content access control' is not set.

So, how do I get node_access in place with Views?

merlinofchaos’s picture

Status: Active » Closed (fixed)

hook_node_access is something that is run after a node is fully loaded. Since Views is loading nodes via query, you can't get the query to respect it unless you also use hook_db_rewrite_sql which is what Views hooks into.

This is not true of just Views, but *any* query that loads nodes. For example, the default Drupal front page will not respect hook_node_access that isn't paired with hook_db_rewrite_sql any better.

hook_node_grants and hook_node_access_records will be used, however, if you set "Disable SQL rewriting" then you are specifically skipping that stuff.

Ludo.R’s picture

Hi,

I believe hook_db_rewrite_sql() is for Drupal 6, but what about Drupal7.
It doesn't exist in api.drupal.org

Should I use hook_query_alter()?

I can't figure out how this hook works : it is called multiple times and I get some query objects, but how to know which one I should alter?
And once identified, how should I actually alter the query?

Could someone give me some feedback?

Thank you!

boby_ui’s picture

Hi,

I tried using the following code and the views does not respect the permission after the content was updated.

=================================================================

if (in_array('contributor', array_values($user->roles))) {

function publishEntities_node_access($node, $op, $account) {

if (in_array('article', node_permissions_get_configured_types())) {

if($node->status==0) {

if ($op == 'update') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_ALLOW;
}
}

if ($op == 'delete') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_ALLOW;
}
}

}
elseif($node->status==1) {

if ($op == 'update') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_DENY;
}
}

if ($op == 'delete') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_DENY;
}
}

}

}
if (in_array('events', node_permissions_get_configured_types())) {

if($node->status==0) {

if ($op == 'update') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_ALLOW;
}
}

if ($op == 'delete') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_ALLOW;
}
}

}
elseif($node->status==1) {

if ($op == 'update') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_DENY;
}
}

if ($op == 'delete') {
if ($account->uid == $node->uid) {
return NODE_ACCESS_DENY;
}
}

}

}

// Returning nothing from this function would have the same effect.
return NODE_ACCESS_IGNORE;
}

}

RumpledElf’s picture

I don't think this is how you're supposed to approach this, but I have a view that only returns a few nodes but through a very complex access function. This worked, and gave me the no results behaviour too.

 function mymodule_views_pre_render(&$view) {
  if ($view->name == 'myview') {
	foreach($view->result as $key=>$result){
		$node = node_load($result->nid);
		if (!node_access('view',$node)){
			unset($view->result[$key]);
		}
	 }
  }
}
Omar Alahmed’s picture

#9 Thank you it's working perfectly.

huslabs’s picture

#9 you are awesome !

vensires’s picture

For anyone getting here from Google or this question...

The only way to properly handle node access and views is using the hook_node_grants() and hook_node_access_records() functions. Using the hook_node_access() function in a hook_views_pre_render() or any other views-related hook being called after the query is executed may have inconsistent effects.

Example:
A query returns 20 results with a pager displaying 10 items per page. The user isn't allowed to see the first 7 results though due to hook_node_access() restrictions (eg. as suggested by #9 above). As a consequence, the first page will only display 3 results, whereas the second page will display 10 results.

I remind that this has nothing to do with malfunction of the views module but because of using it in a way it's just not meant to be for various reasons discussed previously(@see #1 and #6) and/or elsewhere (@see node_access)

sw3b’s picture

Wow #9 is exactly what i was looking for ! Thanks !!!

mikestar5’s picture

#9 is NOT correct, as, if it filters some results, will return an indeterminate number of elements (could be 1, 10 o none).

mpp’s picture

#9 will also result in a faulty pager.

HiMyNameIsSeb’s picture

Correct that #9 will create inconsistencies and break the pager, although for some use cases that might be fine. I am popping my solution here (also commented on the hook_node_grants page) to help anyone else having issues understanding how this works (It took me a while to work it out).

My solution takes group membership into consideration... you can swap that bit out for your computation of whether a user should have access to a node.

Note that my code is for Drupal 8... the hooks themselves are very similar for Drupal 7.

// Remember to include classes you need to use.
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
use Drupal\MODULE\Controller\GroupController;

/**
 * Implements hook_node_access_records().
 */
function MODULE_node_access_records(NodeInterface $node) {
  // When a resource node is saved, give all members access to it.
  // When users join or leave the private group, their permissions need
  // to be updated.
  // These are the access rights used by modules such as views.
  // Note that we should use groupContent to determine membership
  // due to legacy reasons we use this field .
  $group_id = $node->get('field_res_group_id')->getValue()[0]['target_id'];
  $group_members = GroupController::getAllGroupMembers($group_id);

  foreach ($group_members as $member) {
    // Group admins will gain correct access from the override in the
    // hook_node_access function within the custom groups module.
    $grants[] = [
      'realm' => 'resource_custom_access',
      'gid' => $member->id(),
      'grant_view' => 1,
      'grant_update' => 0,
      'grant_delete' => 0,
      'langcode' => 'en',
    ];
  }

  // This grant can be used to give users (admins) access to manage the node.
  // Note the gid is not a user id.
  $grants[] = [
    'realm' => 'resource_custom_access',
    'gid' => 0,
    'grant_view' => 1,
    'grant_update' => 1,
    'grant_delete' => 1,
    'langcode' => 'en',
  ];

  return $grants;
}

/**
 * Implements hook_node_grants().
 *
 * @see MODULE_node_access_records for where these are set.
 */
function MODULE_node_grants(AccountInterface $account, $op) {
  // Use the resource_custom_access to determine the access when loading the
  // node outside a default node view, such as a views query.
  $grants = [];

  if (in_array('administrator', $account->getRoles())) {
    // Allow admins to manage the resource.
    // Gid to view, update, delete
    $grants['resource_custom_access'][] = 0;
    return $grants;
  }

  if ($account->id() != 0) {
    // Otherwise return determine access via the users uid.
    $grants['resource_custom_access'][] = $account->id();
  }

  return $grants;
}
Marceldeb’s picture

I had some troubles in this area and searched a lot. This page seems to be quite high in the Google rankings. So i wanted to add a comment to save a lot of people a lot of time.

If you don't want your view to show items which should not be accessible to a user, think about adding contextual filters first. In my mind, Drupal Views is a sort of a 'Query engine' and you (because of performance) don't want to do access checks on each result. With 100+ results the performance will not be acceptable anymore.

What i did to solve my specific problem: add contextual filters on which you can filter by. This can be for example a group where users are member of using taxonomies. Or relate the 'parent node' which you use to filter on to users. When you have done this, you can add validation criteria to the contextual filter. There you can check if the logged in user has access to the 'contextual filter' node. And if so, he can view the view (and all its resuts). If not, he will see an 'access denied'. You can set all this up in the contextual filters part of views. If you have a sound data model, in 99% of the cases this should be sufficient.

For example, i have a node_type called 'items'. Each item has the fields:
- Title
- Body
- Relation_to_group

I have another node_type called 'groups'
- Title
- Body
- Relation to users

Setup above example (using hook_node_acess) to make sure users can only view / edit 'groups' where they are related to. If you then set up your views, and you want to display 'items', make sure you set up the 'group' as a contextual filter. You can set the validation in the contextual filter to view / edit in the validation criteria fields. If a user opens the 'items' view and provides a contextual filter value of a group where he is a member of he will be able to see all items (because he is member, he can view the group, remember? We did that using hook_node_access). If the user provides a group where he is not a member of, he will not see the view.

In this example only 1 access check is done (on group level). This is a big diffrence comparing that to a solution where each item has to be checked for access. The most important thing is: think about your data model when you start with Drupal...