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
Comment #1
dawehnerThere are two ways to do it:
This two hooks are the system how modules like content access works.
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.
Comment #2
spouilly CreditAttribution: spouilly commentedThanks dereine, I will look into it.
I apologize for the mistakes in the "issue reporting", I have been careless with the tags.
Cheers,
Sylvain
Comment #4
zilverdistel CreditAttribution: zilverdistel commentedThanks!
I used the first approach with hook_node_grants() and hook_node_access_records(), in combination with the "content access" filter in views.
Comment #5
johnvI 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?
Comment #6
merlinofchaos CreditAttribution: merlinofchaos commentedhook_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.
Comment #7
Ludo.RHi,
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!
Comment #8
boby_ui CreditAttribution: boby_ui commentedHi,
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;
}
}
Comment #9
RumpledElf CreditAttribution: RumpledElf commentedI 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.
Comment #10
Omar Alahmed#9 Thank you it's working perfectly.
Comment #11
huslabs CreditAttribution: huslabs commented#9 you are awesome !
Comment #12
vensires CreditAttribution: vensires commentedFor 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)
Comment #13
sw3b CreditAttribution: sw3b commentedWow #9 is exactly what i was looking for ! Thanks !!!
Comment #14
mikestar5 CreditAttribution: mikestar5 commented#9 is NOT correct, as, if it filters some results, will return an indeterminate number of elements (could be 1, 10 o none).
Comment #15
mpp CreditAttribution: mpp as a volunteer and at AmeXio commented#9 will also result in a faulty pager.
Comment #16
HiMyNameIsSeb CreditAttribution: HiMyNameIsSeb commentedCorrect 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.
Comment #17
Marceldeb CreditAttribution: Marceldeb commentedI 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...