Symptoms
When using a node access module alongside Revisioning — Content Access, Taxonomy Access Control, Workbench Access, or something custom — saving a node using the option "Create new revision and moderate" leads to Access Denied or other permissions-related problems. Editing and saving revisions other than the current one can also lead to problems.
Problem
A new revision "in moderation" is effectively a draft that is unavailable to the public. Revisioning performs most of its magic to accomplish this in when loading the node, in revisioning_node_load(). However, when a revision in moderation gets saved, it's that draft revision being passed to hook_node_access_records() instead of the current revision. Node access records are then written based on the wrong revision.
Steps to reproduce
Reproducing this is tricky because a lot of it depends on permissions being configured a certain way. I put together a small module that I hope helps show what's going on. All it does is create a new content type and writes a node access record for nodes of that type. The module writes a node access record only when the node sticky flag is enabled. The record isn't actually enforced, but the Devel Node Access block shows when it's written.
- Create a new site using standard install profile.
- Download and enable: revisioning, devel, devel_node_access
- Patch Revisioning with included patch to add revisioning_issue module, enable it.
- Rebuild permissions as Drupal suggests with "The content access permissions need to be rebuilt."
- Create a new Demo node at: /node/add/demo
Enable "Create new revision, no moderation" under Revision information.
Enable "Sticky at top of lists" under Publishing options. - Save the node, then note the Devel Node Access block in the footer. A node access record has been added for the node. See attached screenshot.
- Edit the node.
Enable "Create new revision and moderate" under Revision information.
Disable "Sticky at top of lists" under Publishing options. - Save the node and then go to the View Current tab.
- Note the Devel Node Access block. The custom node access record no longer appears. This is because it only gets written for nodes with "Sticky at top of lists" and the revision in moderation does not have that. The revision in moderation is being passed to hook_node_access_records() instead of the current revision.
Comments
Comment #1
milesw commentedSomehow the current revision needs to be loaded before node_access_acquire_grants() is called.
The order of hooks invoked during node_save() looks like this:
The attached patch seems to fix the situation, but feels pretty hackish. It loads current the node revision just after hook_entity_update() finishes, as the very first implementation of hook_node_access_records().
Maybe someone can come up with a cleaner solution.
Comment #1.0
milesw commentedStyle edits
Comment #2
Leeteq commentedComment #3
rdeboer@milesw
So sorry this completely dropped off my radar.
I appreciate the analysis that went into this. Thanks so much for the patch!
Like you I'm not 100% about all the ins and outs and potential consequences of your solution.
Would be great to get some feedback from others having applied this patch.
Rik
Comment #4
milesw commented@RdeBoer
No problem. Coming back to this now my description is quite verbose.
Just to clarify what needs fixing:
The current revision of the node needs to be the one passed to node_access_acquire_grants(), not the revision being saved.
Patch #1 accomplishes that by swapping revisions in a first implementation of hook_node_access_records().
Another option would be to swap revisions in a last implementation of hook_entity_update().
Comment #5
rdeboerPatch applied with attribution.
Available now in 7.x-1.x-dev and 7.x-1.7 when released.
Thanks so much Miles and sorry about the delay.
Rik
Comment #6
milesw commentedThanks for getting this committed, Rik!
Comment #7
rdeboerNo thank you, Miles!
Let's run with this for a while in dev. Am keen to have this tested by the community and released as part of 7.x-1.7.
Rik
Comment #9
dsrikanth commentedRevisioning 7.x-1.7 breaks Content Access 7.x-1.2-beta2. Rolling back to Revisioning 7.x-1.6 seems to solve the issue.
The "Access permissions by user" block provided by Devel Node Access shows Revisioning as the reason why a content access configured node is visible to non-intended user. Does anyone else have the same issue?
Comment #10
milesw commented@dsrikanth: I was able to reproduce what you described. Here is the change that's causing the problem: #2191527: Node access is incorrectly restricted
It would be helpful if you could detail your scenario there, including what's happening and what you expect to happen.
Comment #11
fbrooks commentedI wanted to bring #2241705 Redirected to currently published revision after editing unpublished revision to your attention. I believe it is being caused by this patch.
Comment #12
fbrooks commentedMiles/Rik,
I think that if there is an issue here, it is a bug in the individual node access module implementations and not in the Revisioning module.
The demo node access module that Miles provides, I would argue has a flawed hook_node_access_records implementation. The docs for hook_node_access_records states that the node argument is "the node that has just been saved." From that I would expect the node being passed in to reflect the revision that was just saved. If a hook_node_access_records is making a decision based on some other revision, it should use the nid of the node object that was passed in to identify and load the needed revision.
Frank
Comment #13
milesw commented@fbrooks:
That's a fair argument.
The reason I believe this is Revisioning's responsibility is because Drupal core has no concept of "revision in moderation". With core, saving a node revision means the revision you're saving becomes the current node. Previous revisions are simply a history behind the current node. Revisioning changes this behavior, and needs to do some hackish things to accomplish that (see revisioning_node_load()).
Contrib modules expect the behavior included with Drupal core, and therefore something like Content Access can't be expected to handle revisions in moderation.
It's worth noting the Workbench Moderation module offers "revision in moderation" functionality similar to Revisioning. It implements it in a very different way. When a user saves a node revision in moderation everything happens normally, then in a PHP shutdown function it calls node_save() a second time for the "live" version of the node. This updates the node table and also allows the correct node access records to be added for the "current" node revision.
The method used by Workbench Moderation is equally hackish to what's happening here in Revisioning, and I'm not suggesting Revisioning should go that route.
Comment #14
fbrooks commentedMiles,
I see your point. You are right that it is Revisioning's fault these node access modules do not work properly; however, I would say it is not Revisioning responsibility to change to work with them. Revisioning changes the workflow of the system. When you install it, I don't think you should expect it work with a node access module, or any module for that matter, that depends on a different workflow. As a user of Revisioning, or any module that alters the workflow, that is a limitation you accept.
I would propose that the better way to deal with this issue is to make a separate module that adapts Revisioning to the other node access module you wish to use.
Frank
Comment #15
fbrooks commentedThe change introduced in this issue is also causing #2271747 Incorrect Title Used in Status Message When Saving a Revision Under Moderation With Edited Title.
I am concerned that altering the reference to the node revision under save as the patch introduced here does, is going to cause more problems in the long run.
Attached is a patch for resolving this issue without modifying the reference to the node revision under save.
I believe this approach will resolve this issue, #2271747, and both of the issues reported under #2241705
Frank
Comment #16
fbrooks commentedComment #19
fbrooks commentedSomething was wrong with the last line of revisioning_node_access_moderation-2037655-15.patch, submitting again.
Comment #20
fbrooks commentedComment #21
fbrooks commentedThis is another alternative that I think is better. The use of hook_module_implements_alter in revisioning_node_access_moderation-2037655-19 really violates the api for that hook by removing entries from the array of implementations. The api for that hook is pretty clear that changing the order is really the only expected result.
The alternate approach in this patch is to use hook_node_access_records_alter to clear the grants array and rebuild it using the current node. This is clearly within the API for hook_node_access_records_alter and, I believe, it accomplishes the same result as patch 2037655-1-revisioning-node-access-moderation without modifying the reference to the node revision under save.
Comment #22
rdeboerThanks Frank!
Commited with attribution.
Let's hope this is the last of it!
Rik
Comment #23
Leeteq commented- so this is in -dev now (fixed), right?
Comment #24
rdeboerYes it is in the very latest dev.
Comment #26
lostchord commentedI'm still seeing this issue with 7.x-1.9 and 7.x-1.x-dev. I have Workbench Access enabled as well. Rebuilding permissions restores the correct access after the edit.
Comment #27
rdeboerHi @lostchord,
Thanks for sharing your workaround in #26.
Rik
Comment #28
damienmckennaI recommend starting with some tests - document what *should* happen, then work out how to fix it.