Problem
When attempting to use $menuLinkContent->access('randomoperation') an exception is thrown.
The website encountered an unexpected error. Please try again later.
TypeError: Argument 1 passed to Drupal\Core\Access\AccessResult::orIf() must implement interface Drupal\Core\Access\AccessResultInterface, null given, called in /core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php on line 106 in Drupal\Core\Access\AccessResult->orIf() (line 314 of core/lib/Drupal/Core/Access/AccessResult.php).
Drupal\Core\Access\AccessResult->orIf(NULL) (Line: 106)
Drupal\Core\Entity\EntityAccessControlHandler->access(Object, 'clone', Object, ) (Line: 708)
Drupal\Core\Entity\ContentEntityBase->access('clone') (Line: 32)
Cause
\Drupal\menu_link_content\MenuLinkContentAccessControlHandler::checkAccess must always return an object implementing \Drupal\Core\Access\AccessResultInterface. However it incorrectly returns NULL when an access to an operation other than 'view', 'update', or 'delete' is requested.
Motivation
Entity Clone automatically adds a 'Clone' local task to content entities. Patch #2743379: Clone operation shows regardless of permission for Entity Clone updates its clone route to use a 'clone' operation. However because Menu Link Content mishandles unrecognised operations, the menu link page edit form throws an exception.
| Comment | File | Size | Author |
|---|---|---|---|
| #42 | interdiff-32-42.txt | 2.8 KB | jungle |
| #42 | 3016038-42.patch | 3.11 KB | jungle |
| #32 | interdiff-3016038-27-32.txt | 1.18 KB | acbramley |
| #32 | 3016038-32.patch | 2.94 KB | acbramley |
Comments
Comment #2
dpiComment #3
dpiComment #4
jibranComment #5
jibranreroll for 8.6.10
Comment #6
dwwFix is great. Solves a fatal error on some sites where I have entity_clone enabled. Code is simple, clean and proper.
Not sure what test(s) we should write for this. Seems kinda silly to put a test directly in menu_link_content about this particular bug. Yes, it was an oversight in this module, but it seems like it'd be better to have a (series of) test(s) that automatically try custom access operations for all core entity types. I'm just not clear on where those test(s) should live and exactly how they should work.
Otherwise, I'd RTBC this. ;)
Thanks,
-Derek
Comment #7
dpiWorking on tests.
Comment #8
dpiNot sure if theres precedent for anything like this?
Tests implemented as separate class for each entity type, as there is too much differentiation between each for a mono test class.
List of non-internal/non-test core content entities from standard. Tests prove all already work with generic operations, except menu_link_content.
Notably Node already has a kernel test covering unrecognised operations:
\Drupal\Tests\node\Kernel\NodeAccessTest::testUnsupportedOperation.I can easily see these tests breaking silently, if in the future access control handlers shortcut earlier. Not sure there is much I can do to test that.
Interdiff is same as test-only patch.
Comment #10
dwwNice, thanks!
Makes sense to add these as general test classes to flesh out unit coverage for these access control handlers. But they currently *only* test an unrecognized operation. Should we open a meta/plan issue for adding/moving test coverage for the other operations into these? If so, can we add @todo and @see comments about it?
/Tests the/Tests/
(global search/replace, this comment is copied to all the new test classes).
Does it make sense to move these down to the bottom of this method so we can do all the mocking up front? Might be easier to read these tests if they all end with:
I might be wrong, but don't we want to assert this is actually AccessResultNeutral, not just any AccessResult? On quick pondering, I can't imagine why any core entity type should do anything other than ignore operations they don't understand.
In spite of some other differences, this entire function is almost exactly replicated in every other test class you added, except "UserInterface::class" itself.
Makes me think these should all either extend the same shared base class, or at least a test trait. This whole thing is too much copy/paste, not just the comment. ;)
p.s. When you upload patch and test only (either separately or in the same comment), please upload the test-only first, then the full patch. Otherwise, our test bots get confused. You always want the full working thing to be "the last submitted patch".
Thanks again!
-Derek
Comment #12
idebr commentedClosed #3070337: MenuLinkContent access check may not return an AccessResult as a duplicate.
Comment #14
dpiThis came up on another project, so I'm back!
Created a bunch of issues to tackle improvements. Not sure if comments linking to these issues in code makes sense, here they are:
BlockContent and User have equivalent existing tests.
Got it!
Makes sense. Shuffled things around (without inline comment)
I dont think it matters what result we get back. We are not asserting that no other alters impact the result.
Yeh perhaps. Further optimisation can be made in the future with abstracts and traits. Because these are Unit tests, each does the minimum required logic for each implementation to get it working, I'm hesitant to refactor these into a common class.
--
Coming back to this issue after so long, I'm somewhat rethinking whether all these tests are necessary for this issue. Its a bit out of scope.
Comment #15
dww@dpi re: #14: Thanks for moving this forward again!
Thanks for all the linked issues. You forgot to link #3103262: Improve entity access test coverage, the parent, which is useful. Adding that here as a related issue.
Alas, you did /
Tests the/Test/but core conventions are that doc blocks start with action verbs ending in 's'.So now we need:
s/Test/Tests/;) Sorry about NW for this, but it'll save time in the end so core committers don't complain about this.I think it matters a great deal. ;) Anything dealing with access control should be rigorously tested, since bugs usually will count as security holes (access bypass, etc). I don't think it's enough to assert "we got some kind of access result". I think we should assert "we got the correct access result". ;) It's not up to me. But if I were a core committer, that's what I'd think. Whether we should fix this now or later is left as an exercise for the interested reader. ;)
Hrm. I agree further optimization can be a follow-up. However, I don't buy the argument that Unit tests change the nature of the basic desire to put duplicate code in a shared place. DRY. ;) Anyway, I think all this can happen as part of #3103262 and not hold this up anymore.
Thanks,
-Derek
Comment #16
sam152 commentedI agree the scope seems a bit out of hand. Why not fix
MenuLinkContentAccessControlHandler, introduce the test coverage and file follow-ups to prevent regressions on other entity types?FWIW, the code itself looks great, it's just given this has a significant negative impact on sites using common contribs, it would be good to commit this asap.
Comment #17
larowlanAgree
Comment #18
govind.maloo commentedAgree with larowlan. Code changes are looking good we should move this issue forward.
Comment #20
acbramley commentedLooking for consensus on how to proceed with this. I'm happy to prune the patch back to just the MenuLinkContentAccessControlHandler changes and associated test, and move the tests for the other entity types into the issues raised by @dpi. Just want to make sure that's what everyone thinks is the correct approach :)
Here's the cut down patch for now.
Comment #21
mingsongI think it should call the parent's checkAccess() rather than returning neutral as the result directly.
In a scenario where a custom operation is allowed if the user has the default administrative permission.
In this case, it should return AccessResult::allowedIfHasPermission($account, $admin_permission) rather than AccessResult::neutral()
So following codes:
should be replaced with:
Comment #22
boby_ui commentedI used patch #14 for 8.8.8 version and its working +1
Comment #23
acbramley commentedComment #24
acbramley commentedComment #25
acbramley commentedRe #21 I'm not sure that's necessary. Other access control handlers in core do a similar thing such as FileAccessControlHandler (i.e just return neutral).
Comment #26
jungleAgree with this in #21, focusing on MenuLinkContentAccessControlHandler relevant.
I agree with the suggestion in #21
, even though @acbramley replied in #25
Calling parent by default makes sense as the default behavior. And
\Drupal\Core\Entity\EntityAccessControlHandler::checkAccess()returnsreturn AccessResult::neutral();by default too if no opinion.Should start with a verb. "Test" -> "Tests"
Otherwise it's good to go to me.
Comment #27
acbramley commentedThanks for the review @jungle! I've fixed those 2 things up.
Comment #28
phenaproximaLooks good to me. I just have a few suggestions, but no complaints.
This is a really elegant fix.
The test has @coversDefaultClass, so this method should have a
@covers ::checkAccessannotation.Nit: This all works fine, but it might be a little easier to read if we use Prophecy instead. Compare this:
to this:
$entity->language()->willReturn($language)See what I mean?
Comment #29
dwwBritish spelling. We want testUnrecognizedOperation().
Potential false positive if
randomMachineName()randomly returns something valid? ;) Should we hard-code something we know is invalid, instead?As above (#10.4), I still think this isn't great, and we should assert we got the result we expect, not that we got any possible AccessResult. But I'm willing to be out-voted on this. ;)
Comment #30
dwwx-post, but agreed with #28.1 -- I almost flagged the same lines and wrote something similar! ;)
Comment #31
phenaproximaI respectfully disagree :) In this case, the problem isn't that the wrong access result is coming back -- it's that nothing is. I think this test is perfectly covering the scope of this issue.
Comment #32
acbramley commentedFixes 28.2, 29.1, and 29.2
I tried 28.3 but got
Error: Call to a member function willReturn() on nullWill leave as is for now.
Comment #33
acbramley commentedAll child issues created in #14 now have patches on them with the test coverage provided by @dpi + the changes from reviews.
Comment #34
phenaproxima👍 Ship it!
Comment #35
jungleshould not use camelCase naming variables. Thanks!
Comment #36
phenaproximaIf I remember correctly, the coding standards do allow camel case variables, as long as it's kept consistent within the same file. So I don't think we actually need to change anything here; restoring RTBC. (Correct me if I'm wrong!)
Comment #37
larowlanFrom https://www.drupal.org/docs/develop/standards/coding-standards#s-functio...
Comment #38
larowlansaving issue credits
Comment #40
larowlanCommitted 5920411 and pushed to 9.1.x. Thanks!
c/p to 9.0.x
Waiting for 8.9.x test result
Comment #42
jungleThanks for committing, but still uploading the rewrote Test here using Prophecy.
Comment #44
larowlanBecause this is a bug, involves a fatal error, impacts contrib-projects and is non-disruptive, backported to 8.9.x
Discussed the existing follow-up with @jungle in slack as best place for moving to prophecy