Closed (fixed)
Project:
Drupal core
Version:
7.x-dev
Component:
node.module
Priority:
Major
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
5 Nov 2010 at 20:44 UTC
Updated:
3 Jan 2014 at 02:41 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
agentrickardAnd a patch.
Comment #2
agentrickardNote that NO code in core ever calls this function, so I suspect an oversight.
Comment #4
agentrickardOh, Git, how I loathe your diff behavior.
Comment #5
bfroehle commentedIn regards to #2, the API for node_access_view_all_nodes() shows that it is used once. (Edit: On a second reading I realize I am incorrect.)
I don't understand what the issue is here. What sort of functionality isn't working?
Comment #6
johnpitcairn commentedsubscribing for reference
Comment #7
bfroehle commentedThe relevant bit of code disappeared in #701744: node_query_node_access_alter assumes $op, assumes $user, and will only work if alias is "n". In particular see comment number 28.
Again, I don't think there is a bug here. The proposed patch in #2 short circuits some complicated logic below -- at worst it would change access behavior and at best it might be a speed improvement.
Comment #8
agentrickardThat other patch was mistaken. Removing the call to node_access_view_all_nodes() for _all_ $op values is correct. However, it should still be checked when $op == 'view'.
The broken functionality is that node access modules can no longer disable their 'view' restrictions, which was a feature of D5 and D6, and one that is valuable. So accidental removal of the feature is a bug.
For clarity, the primary use-case of such a feature is search, where we might choose to lift normal node access restrictions so that users may see all content. This feature worked in D5 and D6 and is now broken. We are also left with a useless function in core, since it is never used.
Note that no one involved in maintaining a node access module was involved in the prior patch, which was not sufficiently reviewed.
The patch in #2 simply restores existing functionality, which is equivalent to the 'bypass node access' IF check that already short-circuits the logic of this function. And it only changes access behavior when a row in {node_access} exists for NID = 0, which only happens when a) no node access modules are installed, or b) a node access module deliberately uses this (missing) feature.
Comment #9
jhodgdonThis seems rather reasonable, and doesn't break the tests that were put in for the other patch...
Comment #10
agentrickardDo we need to explicitly test for this case?
Comment #11
bfroehle commentedShould this also be checking the value of
$type, i.e.,$type == 'node'?Comment #12
agentrickardVery likely. That must be new.
Comment #13
jhodgdonYeah, that 'type' thing is new, since the patch from the other issue, I believe.
And yes, I think a test would be a good idea. You'd need to make a test class that would cause the view all nodes thing to return true. Since the previous patch broke this behavior, a test would ensure that future patches don't break the behavior.
Comment #14
jhodgdonAlso, in this patch, you need to update http://api.drupal.org/api/drupal/modules--node--node.module/group/node_a... so it explains how to bypass this so that node_access_view_all_nodes() will return TRUE. This information should probably also be added to the node access hooks doc?
Comment #15
chx commentedYou do not need to check node or entity. That argument is only about the base table which is the field data table for entity. If you do not need to add anything access-wise, then you do not need to. Leaving at CNW for #13 and #14
Comment #16
jhodgdonThere is not much point in calling node_access_view_all_nodes() if the base table is not node, though. It would certainly be more efficient to put that into the if() rather than just failing to find something in all those queries?
But actually, shouldn't that check be in http://api.drupal.org/api/drupal/modules--node--node.module/function/nod... -- I mean, why is it calling a node query alter function at all if the base table isn't 'node'?
Comment #17
webchickWe need a test for this.
Also, since this appears to be security related, I believe this makes it a beta blocker, too...
Comment #18
agentrickardI took Sunday off. Will write a test in the morning!
Comment #19
agentrickardUgh. For some reason this test fails. It is behaving as if 'node_test' module is not enabled.
Ideas? I don't have any more time for this today.
Comment #20
jhodgdonI may have time to look at this later today.
Comment #21
bfroehle commentedThe first assert statement
is failing not because node_test.module is not loaded, but instead because
{node_access}has records in it.In tracing through what happens, in
node_access_view_all_nodes()if (!module_implements('node_grants')) {evaluates to
FALSE, so the access query below runs.At this time the content of
{node_access}is:and so
node_access_view_all_nodesthen returnsTRUE.One first problem is that we'll need to call
drupal_static_reset('node_access_view_all_nodes')before each call tonode_access_view_all_nodes().Comment #22
jhodgdonThis test might need to create its own special test module that will pass the view_all_nodes() tests, and not break any of the old tests?
Comment #23
bfroehle commentedAfter poking around, the problem seems to be that
node_access_grants('view')is returning only the default'all' => array(0)array.The problem is not that
node_test_node_grantsisn't returning'node_access_all' => array(0), but instead is thatis emptying the array.
Comment #24
jhodgdonRight. That test class was written with very specific goals. You'll probably be better off writing your own for testing this issue.
Comment #25
bfroehle commentedFor reference, here is a patch that passes the node tests on my computer, however I agree with the others that another test class should be written.
Comment #26
bfroehle commentedIn this patch I've moved the testing routines to a new unit test class. I'm leaving at needs work, because the unit test doesn't successfully capture the actual issue that launched this discussion.
That is, the tests do not check that we ever call
node_access_view_all_nodes()from_node_query_node_access_alter.Unfortunately I don't think I know enough to capture that behavior in a unit test.
Does anybody know why all of the patches aren't getting sent off for testing?
Comment #27
jhodgdonYou have to set the status to "needs review" to cause the test bot to launch tests. Doing that now.
Comment #28
jhodgdonNow setting back to "needs work", see above - the proposed patch is not recommended by the creator.
Comment #29
agentrickard@bfroehle
I tried the static resets, but it's a data storage issue.
That 'all' row should _not_ be present when the new test starts. That may be the cause of the problem. The 0|0|all row of {node_access} should be removed when hook_node_access_records() returns data.
My issue -- which is with simpletest -- is how did you capture the table data in the middle of the test sequence? The whole thing is a black box that just infuriates me.
Comment #30
agentrickardI don't like the idea of adding yet another test module. Part of the confusion right now is that we have two separate modules for running node access tests.
Comment #31
bfroehle commented@agentrickard, in regards to #29:
The easiest way I've found to capture data within simpletest is to have mysql log everything
and follow-up with a liberal use of
sudo tail -f /tmp/mysql.log.Another option is to insert a
die();statement which will kill the testing routine in a specific location. Lastly$this->debug(print_r($variable,1));is useful for probing variables at different stages of the test.Comment #32
bfroehle commented@agentrickard, in regards to #30:
Perhaps this test should be tossed into the
NodeQueryAltertest case then? This may be a better fit than the previous addition toNodeAccessRecordsUnitTest.I don't have a sense for exactly how the node access code is structured, so it's hard for me to suggest a specific test structuring (or even which tests to run). Do you have any examples of code that doesn't function without the original patch in #1?
Comment #33
bfroehle commented#25: 963656-node_access_view_all_nodes-is-never-invoked.patch queued for re-testing.
Comment #34
bfroehle commented#26: 963656-node_access_view_all_nodes-with-new-unit-test.patch queued for re-testing.
Comment #35
agentrickardI have live examples of code, yes. See #965638: Search across all domains fails.
It looks like we have to run node_access_rebuild() during the setup phase of the node access tests; that will kill the 'all' row. Then handling the _alter() problem should be enough.
Comment #36
bfroehle commented@agentrickard The latter part of what you suggest in #35 is essentially the patch in #25.
Comment #37
agentrickard@bfroehle
Nice. I think I got it. Collaboration++
Comment #38
agentrickardI think we want the patch in #26. After discussing in IRC.
Comment #39
bfroehle commentedAs discussed with agentrickard in IRC, we present a patch which does the following:
node_access_view_all_nodes()from_node_query_node_access_alter(). See comments #7 and #8 for a discussion of why this is necessary.node_access_view_all_nodes()to describe its intended use, as per comment #14.node_access_view_all_nodes()is not called from_node_query_node_access_alter(), as per comment #13.Comment #40
carlos8f commentedCode looks good, a couple of text edits though:
Remove extra whitespace after "the". "privileges" is misspelled. Sentences need a period at the end. The sentence is also phrased awkwardly. Perhaps, "Test that the noAccessUser still doesn't have the 'view' privilege after adding the node_access record."
Needs a period at the end.
Comment #41
bfroehle commented#39 + language updates from carlos8f in #40. (Thanks for catching my misspellings and typos!)
Comment #42
catchOne more comment edit:
This could just be "Checks..", the rest is redundant.
I'm not very familiar with node access internals but patch looks sane.
Comment #43
bfroehle commentedI'm going to defer to agentrickard to fix the documentation for
node_access_view_all_nodes(), since he understands exactly what it does better than I do.Comment #44
carlos8f commentedThe doc for
node_access_view_all_nodes()might also want to mention something like:...to make it more clear to developers how to interface with it.
Comment #45
agentrickard@carlos8f that's a nice addition. You think you can roll that and catch's syntax suggestion into a revised patch?
The first change (#42) is really just syntactical, not functional.
Comment #46
carlos8f commentedThis includes the @code example and some revised text.
Comment #47
catchThis looks good, id should be ID though, or possibly nid.
Leaving at CNR, as far as I'm concerned this is good to go otherwise.
Comment #48
carlos8f commented'node ID 0' looked weird, so i went with changing 'node id 0' to 'nid = 0'.
Comment #49
catchI don't see anything else to complain about, the actual code change is tiny, comes with tests, so marking RTBC.
Comment #50
mrsinguyen commented#1: 963656-view-all-nodes.patch queued for re-testing.
Comment #51
webchickAwesome work, folks!
The only thing I'd suggest is that the extra documentation added here probably more belongs in hook_node_grants (which developers will naturally look up when trying to figure out the node access system) rather than here (which they won't). But I'm not going to hold up a critical bug fix (with tests!) over it. :P
Committed #48 to HEAD!
Could we get a quick follow-up that moves the docs location? I would've just done it myself, but figured it best a node access maintainer chime in on the request.
Comment #52
webchick.
Comment #53
agentrickardHere's a doc patch.
Comment #54
jhodgdonThat sounds like your module needs to write something to the database. Please rephrase in terms of what should be returned from which hook, because I don't think we want node access modules writing to this table directly?
I think it's OK to remove this from this function, but maybe a link to the hook doc where you're putting it now would be a good idea?
Comment #55
jhodgdonComment #56
moshe weitzman commentedSorry, I just saw this.
Could someone detail exactly what queries we have added (when node access modules are present). I'm not so sure that this was a deliberate feature in D5/D6 and bringing it back should be avoided if it involves a multi table query.
Comment #57
moshe weitzman commentedx-post
Comment #58
carlos8f commented@moshe what this does is avoids joining the main query with {node_access} if a global view grant exists, and it does a COUNT(*) query on {node_access} (statically cached) to determine if that's the case.
Comment #59
carlos8f commented#218066: Prevent cross posting from reverting metadata fields
Comment #60
agentrickard@jhodgdon In this case, a node access module _must_ write directly to the {node_access} table to enable this feature.
And it's a deliberate feature of the API, otherwise, this would just check the 'all' grant provided by core.
Comment #61
bfroehle commentedIf this is a deliberate feature of the API, I would have expected a way to insert a
nid = 0record into{node_access}via a hook thatnode_access_rebuildcalls.Comment #62
carlos8f commentedI expected the same as @bfroehle. It seems pretty awkward writing a placeholder row to {node_access} *and* returning array(0) with the hook. I would think (although I'm not an expert on the grants system) only one or the other should be necessary.
Comment #63
agentrickardI think it's too late to fix it so that node_access_rebuild() is smart enough to insert the 0 row for you, personally. In < D7, I always had to do this manually.
Comment #64
jhodgdonWell, if that's the case, probably the right thing to do is to fix the documentation so it explicitly says "you need to do this manually" or something like that. If you're sure...
Comment #65
agentrickardThis patch was just about restoring current (expected) behavior and not breaking all the sites that use the feature. The documentation is new, so noting that modules have to handle this row is fine.
This is a very dark corner of the API; and anyone who uses it needs to fully understand what it is they are doing, so making it a little hard to add a 0 row is probably a good thing.
Comment #66
jhodgdonThe point of documentation is to make things clear, not to make things difficult or obscure.
This documentation seems contradictory:
The first paragraph implies you can make the nid=0 grant using hook_node_grants(). Then the second paragraph says you have to put a row into {node_access}. I find this confusing, and apparently (see @62) others do too. Are you really sure that both have to be done? It seems like hook_node_access_records or hook_node_grants should take care of this.
And again, please link from where you took this documentation out in node.module to this hook doc, so someone can find the documentation there.
Comment #67
jhodgdonOh... I see, these paragraphs are correct, but they're still *extremely* confusing and I think they need to be rewritten.
Comment #68
bfroehle commentedI've taken a stab at rewriting the documentation for this feature. This needs review from both a clarity perspective and an accuracy perspective, since I do not claim to be an expert in this particular matter.
Happy BAD Camp everybody.
Comment #69
agentrickardI like it. I made a minor grammar / clarity adjustment inside hook_node_grants() and added some specifics about how node_access_view_all_nodes() is applied.
Comment #70
bfroehle commentedA few more grammar adjustments.
Comment #71
bfroehle commentedI still don't quite understand this paragraph.
Comment #72
agentrickardIt's intended to be a note about what the function does, since the name might be misleading. If it adds confusion rather than clarity, then it should be removed.
Comment #73
bfroehle commentedHow about:
Comment #74
bfroehle commentedAnd since I just woke up, I guess I attached a patch which does not actually match the text of my comment in #73. Fixed here.
Comment #75
moshe weitzman commentedEr, rename the function instead if a wall of text explaining its shortcomings?
Comment #76
jhodgdonIt would be good to clue the developer in about how they would do this.
What happened to the code changes and/or tests in this latest patch? I'm confused...
Comment #77
bfroehle commented@jhodgdon in #76: The code changes and tests already were committed by webchick in #51. At this point we're just working on fixing up the documentation so this 'very dark corner of the API' as agentrickard called it in #65 is a little less dark.
@moshe in #75: That seems like a good solution. Or perhaps I am totally mis-understanding the point of the function --- this is not my area of expertise.
Comment #78
jhodgdonoh, sorry! I forgot (and am a bit out of it today).
Comment #79
jhodgdonI'm going to take a stab at this. I don't think the current patch explains things quite right...
Comment #80
jhodgdonI think this whole function is incorrect, besides the fact that the documentation is confusing or wrong.
Take a look at:
http://api.drupal.org/api/drupal/modules--node--node.module/function/nod...
It calls node_access_grants('view') to figure out which realms/gids apply to the current $user, even though http://api.drupal.org/api/drupal/modules--node--node.module/function/_no...
allows the query to pass in a particular $account, which is used for the other permissions checks.
So I think that node_access_view_all_nodes() function needs a rewrite. It needs to have a $account parameter, and if it's going to cache its information, it needs to cache on a per-account basis.
The patch in #51 that was committed is wrong.... or am I missing something?
Comment #81
jhodgdonComment #82
carlos8f commentedBumping to critical again, possible problem with the commit. This permission (view all nodes) is very general, so I'm not certain how big of a bug this is. But node_access_view_all_nodes() implicitly does only operate on the current user, which does not respect the query's account in $query->getMetaData('account'). This might be why it was left out in the DBTNG refactor. Let's get more eyes on this to determine how big of a problem this is. If it's a problem, we need another API change.
Comment #83
bfroehle commentedTo get the ball rolling, here's a patch which adds an $account to node_access_view_all_nodes(). The result is statically cached only if $user == $account.
Needs review to trigger the test bot.
Comment #84
bfroehle commentedBack to needs work. At a minimum, the patch in #83 leaves out all of the changes to the comments as discussed between #53 and #79.
Comment #85
jhodgdonThe caching should be done for all $account->uid, in an array, I think. Otherwise, this function is a big performance hit.
Comment #86
bfroehle commentedjhodgdon: Do we cache for all users anywhere else? What if this somehow gets called for every user in the database?
Comment #87
carlos8f commentedLooking back at the D6 code, there is no regression I can see. node_access_view_all_nodes() is part of the archaic node_db_rewrite_sql(), and uses $GLOBALS['user']. Respecting $account can't be a bad thing though, although I'm not sure what real difference it makes if all we're checking for is a global view grant.
Comment #88
bfroehle commentedAlternatively we could choose only to invoke node_access_view_all_nodes() from _node_query_node_access_alter() if $user->uid == $account->uid. I'm not sure what effect this would have on the contributed node access modules.
Comment #89
jhodgdonAs far as the caching goes, the problem is that if someone is doing a node query and has node access modules, and the node query is against an account other than the global $user, this function could be called several times... or maybe not, but if it isn't called several times, why bother with caching at all?
My point is, either cache for all the calls, or remove the caching.
Deciding not to call node_access_view_all_nodes if the $account is wrong is not the right answer. That would break the desired functionality.
Comment #90
bfroehle commentedThis patch caches all results in an array keyed by $account->uid.
I also updated the unit test to ensure node_access_view_all_nodes is receiving the proper $account and not using the global $user.
Again, still need to work in revised documentation in #53 to #79.
Comment #91
agentrickardThe patch in #51 was a straight port of D6 (and earlier) functionality that was accidentally removed with the DBTNG implementation of query_alter() and broke one of our D7 sites.
This patch went in an interesting direction after that, and I hope it's not too late for this (minor) API change. Two things to note about this new approach:
1) It is now selective per user, which is a Good Thing for security.
2) The use of drupal_static() gives us a reset, which, unlike D6, will actually let us enable this for a query inside a block but not for the rest of the page call, which the simple static $access in D6 did not allow. That's a really interesting DX win for some of us. The $access array based on account follows what node_access() itself does.
I love this new patch, though I was skeptical at first.
Not sure there is a better name for this function, though something with 'list' might be helpful, since this applies to listing queries.
Comment #92
carlos8f commented#90 looks good. The bug itself is minor, just an oversight (or technical limitation?) that has been in core from the early days. Marked critical only because the API change should be in before RC. If #90 looks good to go, we can get that in, demote back to normal, and work on the documentation as a follow-up.
Comment #93
jhodgdonThe patch's new doxygen block is not formatted correctly -- needs a blank line between the @param and the @return. I also don't like what was written for the @param much:
How about "The user object for the user whose access is being checked", rather than "performing the operation"?
But since we're back to patching code, and we do plan to fix up ALL the doc after the code is OK, I wouldn't hold up the patch for this -- just add it to the rest of the doc fixup.
Comment #94
bfroehle commentedI've rolled another patch which attempts to combine the documentation and fix. (963656-patch-and-documentation.patch).
The additional changes since #90 are in 963656-diff-since-90.patch.
Comment #95
jhodgdonWhen you do interdiffs, just call them something.txt, so that the test bot doesn't choke on them.
I still think the doc needs some love and attention, because it's very confusing as it is... I may have time later today to take a stab at it.
Comment #97
tstoecklerRerolled the patch. Didn't review or anything, just trying to make it pass for now.
Comment #98
tstoecklerComment #99
tstoecklerDoube word (other).
Minor, but couldn't/shouldn't
global $userbe inside the if-statement?Powered by Dreditor.
Comment #100
jhodgdonI cleaned up the documentation a bit in the previous patch, and hopefully it's a bit clearer. Didn't touch the code.
And by the way, I'm generally in favor of putting globals at the top of the file rather than inside some if statement. It's easier to find them that way, and avoids the possibility that they might be needed again and be confused with a local variable elsewhere in the function.
Comment #102
catchFor globals I personally prefer either the top of the function, but even better than that is just using $GLOBALS['user']; - for D8 I want to file a code style issue to make that consistent.
Comment #103
bfroehle commented#100: 963656-100.patch queued for re-testing.
Not sure why this just failed, especially since it only differs from a previous passed test in comments only. Queued for retesting.
Also, I'm supportive of @jhodgdon's documentation changes. I think we've kicked around this issue enough, at least once it passes the test bot.
Comment #104
bfroehle commentedOkay, #100 passed this time. No idea why the testbot coughed a bit previously. I'm happy with the patch in #100. +1 RTBC.
Comment #105
jhodgdonI'm +1 on RTBC too.
Also note that this is a (needed) API change from the currently committed code (tagging).
Comment #106
agentrickardI love it when doc fixing leads to code fixing. I'll take the plunge and RTBC this.
Comment #107
webchickSorry, but unless someone can make the case for adding $account to node_access_view_all_nodes() being some sort of critical security regression, it's too late for this change. AFAIK that's been hard-coded since like Drupal 3.
Documentation improvements look good though.
Comment #108
carlos8f commented@webchick why set this to needs work?
This is just a simple improvement, now that we have the $account stored on the query's meta we can use it in the access check (unlike D6 and earlier). I don't see why it's too late.
I think this qualifies as major at least; it would prevent queries run under the anonymous global $user (cron, or in some cases drush) from having proper node access, only showing itself when you have a node access module installed. In that light it is not "bugs that affect one piece of functionality" (normal) and rather "issues which have significant repercussions but do not render the whole system unusable" (major).
Comment #109
jhodgdonNot having $account is a security bug, since the node access query alter (the ONLY place this function is invoked) has an account associated with it that's not necessarily $user.
In D6, http://api.drupal.org/api/drupal/modules--node--node.module/function/nod... didn't have any way to specify the account.
Comment #110
webchickAh, ok. That makes sense, thanks. Sorry, it read more like "oh, while I was at it I found this!" and of course we're way past being able to commit patches like that now. :)
However, seems to no longer apply.
Comment #111
bfroehle commentedSo.. does this mean we need to then verify $user->uid == $account->uid in _node_query_node_access_alter()? Otherwise this is, possibly, a security issue?
Comment #112
agentrickard@bfroehle
I don't believe so. It is the query-builder that is responsible for providing alternate $account data. Otherwise, global $user is assumed in _node_query_node_access_alter():
The only places in core that seem to add 'account' to the metadata are tests, however.
Should still be RTBC. Marking for security review.
Comment #113
carlos8f commented#100: 963656-100.patch queued for re-testing.
Comment #115
carlos8f commented#100 was committed accidentally with the node type cache patch: http://drupal.org/cvs?commit=453544. Since #100 was RTBC, I am tentatively setting this to fixed.
Comment #116
rfayOK, so does this break backward compatibility? Does it need to be announced? If so, please give a description of the impact on people.
Comment #117
carlos8f commented@rfay, this should have a very minimal impact API wise, does not affect backward compatibility, and simply fixes the bug that if $account "meta" was set on a node access query, we use that instead of $GLOBALS['user'] when checking for a global view grant. node_access_view_all_nodes() works the same way as it did in D6, but now takes an optional $account argument.