The documentation for hook_node_access() does not reflect what is in the current code.

The enforcement of create access has moved to hook_ENTITY_TYPE_create_access(). Node module doesn't use node_node_access() for permissions, instead using NodeAccessControlHandler::checkCreateAccess().

This patch:

  • Updates the documentation of hook_node_access().
  • Updates the documentation of node_node_access().
  • Adds a negative test case to NodeCreationTest to ensure that create access permissions are handled securely.

Issue fork drupal-2348203

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dawehner’s picture

Well, hook_node_access() should just be executed in case you have a single node, not when you have a list of entries.

agentrickard’s picture

It's also not invoked for user 1 or roles that can Administer content.

However, it does appear that the 'create' operation is no longer invoked by hook_node_access().

Adding a die() statement here should cause \Drupal\node\Tests\NodeCreationTest to crash, but it does not.

Tracking through the code it looks like the work is handled by different hooks in D8.

From EntityAccessHandler.

    $access = array_merge(
      $this->moduleHandler()->invokeAll('entity_create_access', array($account, $context, $entity_bundle)),
      $this->moduleHandler()->invokeAll($this->entityTypeId . '_create_access', array($account, $context, $entity_bundle))
    );

Though I can't see where Node module invokes these, either, so I may just be misreading.

agentrickard’s picture

Status: Active » Needs review
agentrickard’s picture

Title: hook_node_access isn't invoked » Documentation: hook_node_access() no longer fires for the 'create' operation
Priority: Normal » Major
Issue summary: View changes
FileSize
4.51 KB

Here's the patch. Updating the issue summary with details.

agentrickard’s picture

@chx points out that 'existant' is not a word ;-p.

chx’s picture

Title: Documentation: hook_node_access() no longer fires for the 'create' operation » hook_node_access() no longer fires for the 'create' operation

This is no longer a documentation-only patch. The mention of Drupal 8 in doxygen is very odd, ag ' \* .*Drupal 8'|grep -vi 'deprecated\|test' tells me this is simply not done elsewhere, the @see should be enough and https://www.drupal.org/node/1862420 should be amended to include the new hook and altogether made cleaner.

agentrickard’s picture

This is already documented on that page:

A default entity access controlhandler (Drupal\Core\Entity\EntityAccessControlHandler) is provided that takes care of a static cache for access checks and also invokes a hook named after the entity_type (hook_{$entity_type}_access(EntityInterface $operation, UserInterface $account, $langcode)). This follows the same pattern as hook_node_access() in 7.x. If no hook implementation returns something, checkAccess() is invoked.

Docblock update to remove the Drupal 8 reference.

chx’s picture

Yes but the hook_ENTITY_TYPE_create_access hook is not mentioned AFAICS

agentrickard’s picture

I see. I think the above quote is a typo.

agentrickard’s picture

Updated docs page.

chx’s picture

Status: Needs review » Reviewed & tested by the community
catch’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/node/node.module
@@ -904,8 +907,6 @@ function node_node_access(NodeInterface $node, $op, $account) {
-    case 'create':
-      return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
 

While I don't think it will ever happen in practice, someone could theoretically call node_node_access() directly with $op = 'create'.

This makes me think we should split this hunk (and only this hunk) out to an 8.1.x follow-up.

chx’s picture

Why? It's a hook implementation, you are absolutely not supposed to call those.

catch’s picture

Yes, you're not supposed to do lots of things.

We decided to commit things that break code doing things it should not for minor releases, unless it's necessary to fix a bug.

That way, if you have a site that has a module that does lots of horrible things, it breaks once every six months (or if we're lucky, just once when 8.1.x comes out then never again when the module is fixed), not every month.

This patch is a fairly extreme example, but the dead code can be removed in 8.1.x as early as tomorrow, so it doesn't hurt much to leave it dead for another 4.5 months in 8.0.x, then the entire branch will be lopped off.

Then in the unlikely case some custom module somewhere is re-animating that dead code, it'll blow up in April instead of January.

agentrickard’s picture

Shouldn't we at least push the documentation portion of this patch? This was a huge point of confusion for me when working on module ports.

catch’s picture

Well I'd commit the whole thing if the hunk pointed out in #12 was split to an 8.1.x follow-up...

agentrickard’s picture

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Berdir’s picture

Status: Needs review » Needs work

Unfortunately 8.1.0 is out now, so with the same argument, we need to update the @todo to 8.2 now?

Or we switch to 8.2.x, do it and backport without that part.

If we're going to touch node_node_access() and removing one case is OK then I would even consider to remove the hook entirely and move that remaining logic into checkAccess() where everything else is checked.

Berdir’s picture

Also not sure this is a (major) bug, there is no functional problem here, just wrong documentation and dead code?

xjm’s picture

Issue tags: +Needs change record

Thanks @Berdir.

+++ b/core/modules/node/node.module
@@ -906,6 +909,7 @@ function node_node_access(NodeInterface $node, $op, $account) {
+    // @TODO: The 'create' operation is deprecated and will be removed in 8.1.

Let's actually just remove the "and will be removed in 8.1" from this @todo. It should be formatted like:

// @todo The 'create' option is deprecated. https://www.drupal.org/node/followup

Then we can commit the docs fix to both branches now and consider what @Berdir suggests in a followup. The test case suggested in the summary also sounds like a good followup.

I think we should also add a CR for this to communicate that this was previously changed.

The case I would see for this to still be major is if the result of following the documentation would be information disclosure when the hook didn't fire. Is there a scenario where that would happen? E.g. if some other implementation allowed create access, but your implementation was trying to override and forbid it.

dagmar’s picture

Version: 8.1.x-dev » 8.2.x-dev
Status: Needs work » Needs review
FileSize
4.48 KB
590 bytes

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Berdir’s picture

Status: Needs review » Needs work
+++ b/core/modules/node/node.module
@@ -907,6 +910,7 @@ function node_node_access(NodeInterface $node, $op, $account) {
 
   switch ($op) {
+    // @todo The 'create' option is deprecated. https://www.drupal.org/node/2730439
     case 'create':
       return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');

We discussed that at the entity field issue triage meeting in Dublin.

It is not deprecated, it does not exist. And we do not support calling hooks directly, so we can just remove this.

the only thing we didn't agree on was if it can be removed in a patch or a minor release.

mohit_aghera’s picture

Updating patch for 8.2.x branch.
Removing comment as mentioned by @berdir

xjm’s picture

Issue tags: +Triaged core major

We also did agree in Dublin that this issue is major, because by documenting access control that does not actually work it could even lead to information disclosure for what appears to be "supported" code.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

I don't care if it should be a patch or a minor release, doesn't matter, better to get it into 8.7 than waiting another 2 years :)

Berdir’s picture

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

This patch still needs a CR and unfortunately it does not apply :(

govind.maloo’s picture

Status: Needs work » Needs review
FileSize
3.75 KB
walii’s picture

i think this hook work only for "anonymous" role

walii’s picture

if any role have permission "Bypass content access control" , this hook not call

Berdir’s picture

No, this works for everyone (except create access), might there might be caching involved.

Edit: Yes, the bypass permission bypasses everything including this hook. That's not the same as only for anonymous users, though :)

@alexpott: Not sure what you want to do in an CR. We have a CR for entity access that also documents that create access is separate since 2012 :) https://www.drupal.org/node/1862420.

alexpott’s picture

Status: Needs review » Needs work
Issue tags: -Needs change record, -Needs reroll

@Berdir I was just making sure that we were not avoiding taking #21 in account. It's great that a CR already exists.

+++ b/core/modules/node/node.api.php
@@ -328,15 +331,14 @@ function hook_node_grants_alter(&$grants, \Drupal\Core\Session\AccountInterface
   switch ($op) {
-    case 'create':
-      return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
-

I would add a

// Note create access is handled by hook_ENTITY_TYPE_create_access().

above switch ($op) because it helps explain why create is not in the list without having to read the methods docblock.

govind.maloo’s picture

Status: Needs work » Needs review
FileSize
4.38 KB
Berdir’s picture

I think @alexpott meant "Node" instead of "Note". Note could also be an option, but then it would need to be "Note that...".

Also, make sure you provide an interdiff, that makes it easier to review the difference: https://www.drupal.org/documentation/git/interdiff

alexpott’s picture

I meant Note - the that is superfluous but it can be added so // Note that create access is handled by hook_ENTITY_TYPE_create_access(). is great.

Berdir’s picture

Status: Needs review » Needs work

Ok, needs a reroll anyway.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

alexpott’s picture

#3061217: Clarify if hook_node_access() $node argument can or cannot be a string is a duplicate of this but it also contains an important fix to hook_node_access() docs that's not here.

Also I ponder if we should be maintaining hook_node_access() docs in node.api and not pointing to the docs in entity.api.php.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

init90’s picture

Here is update with changes according to #44

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

mxr576’s picture

Status: Needs review » Reviewed & tested by the community

I don't care if it should be a patch or a minor release, doesn't matter, better to get it into 8.7 than waiting another 2 years :)
@berdir (28 November 2018 at 21:35)

and here we are... =]

So my colleague, @boobaa, hast just bumped into this dead code related to the "create" operation in node_node_access(). He actually spotted the missing break after the "create" case in the switch, long story short, we unnecessary spent time figuring out that how this bug is not discovered by any core tests...

How can we help to get this fix in core?

alexpott’s picture

Title: hook_node_access() no longer fires for the 'create' operation » [backport] hook_node_access() no longer fires for the 'create' operation
Version: 9.1.x-dev » 9.0.x-dev

I deliberated about the changes to node_node_access and whether they'd have any impact. Some places in contrib are calling this directly!!! I'm guessing never with an op of create because it is totally broken. Also there are places in contrib that say "copying from node_node_access" which is a very good reason to fix this broken code. I also agree with @Berdir who has pointed out that that node_node_access shouldn't really exist but that can be done in a task follow-up.

@catch did ask for that hunk to be broken out before BUT in the interim things have got even worse and the $access from the create is not even being used. Atm if you call node_node_access with an op of create it will return you the access result for edit permissions. That is totally wrong.

I'm going to commit this to 9.1.x but I believe it should be backported to 8.9.x as this code has gone from wrong to dangerous.

Committed aa8d81c and pushed to 9.1.x. Thanks!

diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index fb446bbd4e..8461d91139 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -8,7 +8,6 @@
 use Drupal\node\NodeInterface;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Xss;
-use Drupal\Core\Access\AccessResult;
 
 /**
  * @addtogroup hooks

Removed unused use on commit.

  • alexpott committed aa8d81c on 9.1.x
    Issue #2348203 by agentrickard, govind.maloo, mohit_aghera, dagmar,...
alexpott’s picture

Title: [backport] hook_node_access() no longer fires for the 'create' operation » hook_node_access() no longer fires for the 'create' operation
Version: 9.0.x-dev » 8.9.x-dev
Status: Reviewed & tested by the community » Fixed

Discussed with @catch and we agreed to backport this to 9.1.x given the changes since the last rtbc.

  • alexpott committed c804dae on 9.0.x
    Issue #2348203 by agentrickard, govind.maloo, mohit_aghera, dagmar,...

  • alexpott committed 57fe612 on 8.9.x
    Issue #2348203 by agentrickard, govind.maloo, mohit_aghera, dagmar,...

Status: Fixed » Closed (fixed)

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

pratik_kamble made their first commit to this issue’s fork.