Subscriptions, and perhaps some other applications, need to use the uid argument of node_access to determine if a node will be accessible by another user; in the case of subscriptions, to determine if a subscriber should be informed of an article or comment posting to a taxonomy term or other category posting.

However it appears that node_access, plus downstream components such as user_access or module level access hooks, do not observe and respect the uid parameter. Therefore one can not accurately determine if a user (other than the current user) will have access to a node or not.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

radagast-1’s picture

I am working on an SSE feed module for Drupal and have run into the same problem. There does not seem to be a way for one user (even the main admin or a module) to generate content for another user (even the anonymous user) because there is no obvious way to determine what will be visible to another user. This seems to mean that content can only be generated on the fly when the user viewing it is waiting. This can be a problem for cached feeds and such like.

Is there a "become" function where a module can change the current user to another user temporarily? Would it be safe to muck with the $user global variable and then restore it later?

magico’s picture

Version: 4.7.2 » x.y.z

Can some code (or directions) be provided to help following this possible bug? Is this a core problem?
Seems so abstract that I'll bet that if this is a bug it will be present in HEAD.

urbanfalcon’s picture

I'm having this problem as well. I didn't notice until I utilized simple_access for a category being managed through subscriptions.module (http://drupal.org/node/13502). Folks are getting subscription emails on nodes for which they don't have 'view' access, even though there's a check in subscriptions for just that.

urbanfalcon’s picture

Got it! If the user has admin privileges and they trigger the node_access function, *they* (the admin users) are the reason that it is ignoring individual user privileges. Here's why:

<?php
function node_access($op, $node = NULL, $uid = NULL) {
  // Convert the node to an object if necessary:
  if ($op != 'create') {
    $node = (object)$node;
  }
  // If the node is in a restricted format, disallow editing.
  if ($op == 'update' && !filter_access($node->format)) {
    return FALSE;
  }

  if (user_access('administer nodes')) {
    return TRUE;
  }

  if (!user_access('access content')) {
    return FALSE;
  }
?>

See how it returns TRUE if the user triggering the function has "administer nodes" permissions? It's not even taking into account what the permissions are of the $uid that's been passed in. One short fix, and...

<?php
function node_access($op, $node = NULL, $uid = NULL) {
  // Convert the node to an object if necessary:
  if ($op != 'create') {
    $node = (object)$node;
  }
  // If the node is in a restricted format, disallow editing.
  if ($op == 'update' && !filter_access($node->format)) {
    return FALSE;
  }

  if (!$uid && user_access('administer nodes')) {
    return TRUE;
  }

  if (!$uid && !user_access('access content')) {
    return FALSE;
  }
?>

This seems to have a logical basis, and so far (from what I've seen), the corrections are having the intended effect.

pwolanin’s picture

Should that be:

!isset($uid)

rather than

!$uid
chx’s picture

Status: Active » Needs review
FileSize
1.06 KB

There. IMO Moshe should review this before commit.

chx’s picture

FileSize
5.01 KB

So Moshe looked at the issue and asked what about all the hook_access implementation and he is right.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

i did some testing and all still works fine ... not sure when we broke this but i'm sure happy to have it fixed.

chx’s picture

FileSize
720 bytes

We never broke this -- this never worked! The history of this $uid is that node_access_grants (removed in 4.6) once had a $uid parameter thus Gerhard added $uid to node_access in http://drupal.org/10884 . But it was never implemented properly, it was only passed on to node_access_grants

So. An alternative to fixing hook_access deep into the code freeze is to remove $uid from node_access by applying the attached patch and apply the above fix in the Drupal 6 cycle. Those who need this functionality will need to change users -- yuck. But, they needed to change users in the past, so actually -- no change.

It's up to the powers above me to decide where they thaw the code freeze -- at node_access or at hook_access.

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed chx' last patch. If it has been broken for that long, we can do another release without it.

Anonymous’s picture

Status: Fixed » Closed (fixed)
chx’s picture

Version: x.y.z » 6.x-dev
Category: bug » task
Status: Closed (fixed) » Needs review

#7 could be candidate for Drupal 6.

keith.smith’s picture

The patch in #7 does still apply to HEAD

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
6.14 KB

this is a reroll of #7. it had 1 junk failed in node_content_access().

i tested and the patch works as advertised. this is actually a really important change. without this, modules have to impersonate users before sending them an email notification, for example. thats the only way for a module to check if recipient has view access for the node.

Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Needs work

How come a $uid get into node_access? I don't see any $uid variable used in node_access neither at api.drupal.org, neither my latest cvs HEAD checkouts.

pwolanin’s picture

patchin #14 fails on book module since book_access() no longer exists.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
5.11 KB

re-roll without the book module part - also, Gabor makes an astute observation that the $uid param was removed from node_access() in a previous patch. So, this adds it back

Gábor Hojtsy’s picture

I tried to look up the blame logs to find a reason why this was actually removed, but it is not behaving like I expected it to :] It would be good to see the issue. Anyway, It would be inconsistent to have $uid here, and $account at other hook_access() functions.

moshe weitzman’s picture

uid was part of the function since the beginning but it had a bug which was discovered very late in the release cycle (5.x i think). so rather than fix the bug and possibly create more breakage, we decided to remove the feature since it never worked ... so, lets get it back in.

pwolanin’s picture

The 4.7.x versions of node_access still has $uid as a param, so I just went back to that.

Also, here it is the really is the user ID, not the user object - so the difference in variable names is correct. See in the patch:

+  $account = isset($uid) ? user_load(array('uid' => $uid)) : $GLOBALS['user'];
Gábor Hojtsy’s picture

Yes, I understand there is a difference now, but why should there be a difference? As far as I see, it looks confusing/inconsistent.

pwolanin’s picture

@Gabor - are you suggesting that the 3rd param should be a user object rather than a user ID? Your comment is still not making sense to me.

Gábor Hojtsy’s picture

I did not take sides, because it matters less, how it is made consistent, but it matters a lot more to have it consistent. If the user id is enough for all places, then lets just have the user id. If the user object is required at some, then have that. If that is too complicated to have the user object for some calls, then we might need to think about this more, but then otherwise go with the simplest *and* more consistent solution.

Excuse me if this direction was not clear.

pwolanin’s picture

since the calls to user_access() require a full user object - if we are going to insist on consistency, it makes more sense to pass in the loaded object.

pwolanin’s picture

FileSize
5.11 KB

ok - node_access() takes $account now - anyone want to test?

chx’s picture

If the SQL in node_access does not change -- or at least the grant invoke -- this can't be right.

pwolanin’s picture

Status: Needs review » Needs work

chx - you are correct - function node_access_grants() now has $uid as a param, but that is not passed in, and we ought to pass in the already-loaded user object.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
7.83 KB

ok, I think this one is more thorough.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
7.39 KB

Patch still applies with some fuzz. This reroll removes the fuzz.

I tested the patch while running the node_access_example module and all is well. Then I ran the following fragment by passing different nids and got the expected results.

return node_access('view', node_load(1), $GLOBALS['user']);
Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Needs work

Generally looks good as far as I see. But

- there is a small whitespace issue in the phpdoc added to node_access()
- we already global $user in node_access (do we use it somewhere there?), so why use $GLOBALS['user'] later on in the function?

moshe weitzman’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
7.44 KB

Fixes both problems mentioned by Gabor.

Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Needs work

Because this question was not answered, now I looked into node_access() and it does seem to use $user to give permission to the author to view a node even if not published yet. That should be affected by the $account param, right?

moshe weitzman’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
7.71 KB

fixed ... good catch.

Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Fixed

Thanks, committed.

DrupalTestbedBot tested moshe weitzman's patch (http://drupal.org/files/issues/mw_91.patch), the patch failed. For more information visit http://testing.drupal.org/node/118

DrupalTestbedBot tested moshe weitzman's patch (http://drupal.org/files/issues/mw_92.patch), the patch failed. For more information visit http://testing.drupal.org/node/125

DrupalTestbedBot tested moshe weitzman's patch (http://drupal.org/files/issues/mw_93.patch), the patch failed. For more information visit http://testing.drupal.org/node/132

Robin Monks’s picture

(patches that are already committed will come back failed, since you'd be patching HEAD twice)

moshe weitzman’s picture

I added a note to update docs about this since some modules like project_issue and og can now check access before sending email notifications (for example).

@RobinMonks - it would be a great if the Tester could check the project status before running a test and posting irrelevant results (i.e. issue is in fixed state).

Anonymous’s picture

Status: Fixed » Closed (fixed)