Problem/Motivation
Menu links are always rendered for any user with the link to any page
permission. This permission should not control the display of menu links. This results in (for example) users with the permission seeing menu links that they do not have access to the route of, or any children of them. A common example of this is seeing Configuration, Structure, Extend, Appearance, etc menu items without having permission to those pages. (see #50)
Original report
In #2323721-24: [sechole] Link field item and menu link information leakage this check was added.
This is caused by following code \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators::menuLinkCheckAccess()
protected function menuLinkCheckAccess(MenuLinkInterface $instance) {
if ($this->account->hasPermission('link to any page')) {
return TRUE;
}
// Use the definition here since that's a lot faster than creating a Url
// object that we don't need.
$definition = $instance->getPluginDefinition();
// 'url' should only be populated for external links.
if (!empty($definition['url']) && empty($definition['route_name'])) {
$access = TRUE;
}
else {
$access = $this->accessManager->checkNamedRoute($definition['route_name'], $definition['route_parameters'], $this->account);
}
return $access;
}
In D7 there was hook_translated_menu_link_alter()
but it was removed according CR https://www.drupal.org/node/2226481
So there's no way except overriding menu.default_tree_manipulators
service (protected method) to hide menu links.
Background: working on masquerade module port for 8.x I need to hide Unmasquerade link while there's no flag in session pointing that user masqueraded.
Proposed resolution
Remove this check, and try to separate render access permissions from route.
Add a new menu tree manipulator to menu_ui so that inaccessible menu items can still be managed in the backend
Remaining tasks
Discuss
Patch
Fix failures
User interface changes
Menu links could be hidden for users with link to any page
permissions and UID1 too.
API changes
New MenuUiMenuTreeManipulators
to allow access to all menu items when in the context of menu_ui
Comment | File | Size | Author |
---|
Issue fork drupal-2463753
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:
- 9.5.x compare
- 10.1.x compare
- 2463753-menu-9.5 changes, plain diff MR !3118
- 2463753-menu changes, plain diff MR !2978
Comments
Comment #1
andypostComment #2
dawehnerI agree that there is no point in using link to any page for checking the menu tree entires themselves.
We had a regression, let's ensure that we have such a link which is hidden for the user 1.
Comment #4
andypostThe link and route is going to be added in #2463659-6: Regression test coverage: integration test for an uncacheable menu link that depends on session data
Comment #5
Crell CreditAttribution: Crell at Palantir.net commentedIf memory serves, that permission was added to allow site builders to build out their IA before a site is fully built. Eg, add links to /about, /products, /etc before the views/panels that provide those pages exist. The challenge is that you can't add a link to a page you can't view (as a security measure), but that in turn means that since you can't access a non-existent page you can't add that link. That regression was introduced in D6 but not fixed until D8.
The permission in question is primarily intended as a loophole for site builders to do the above. Would there be a better way of handling that? Removing that check would be a regression at this point (do we regress a regression?), so we'd need some other way to ensure that site builders have that ability.
Comment #6
andypost@Crell thanx for pointer, so this code should be like new patch but else is questionable
Comment #8
dawehnerIf we don't know how to get the else, we also don't know how to get to the elseif before, don't we?
Comment #9
dawehnerWorking on tests for now.
Comment #10
dawehnerThe tests are now working pretty fine for the block page.
One open question is how we should handle the menu UI.
You want to show the user/login link there as well, but at the same time, given that 'administer menu' is not a restricted permission, we kinda have a problem here.
Maybe special casing user/login is the right thing to do.
Comment #12
Wim Leers#1805054: Cache localized, access filtered, URL resolved, and rendered menu trees landed, this needs a reroll.
P.S.: Another benefit of this patch, is that it will remove the need to have the
user.permissions
cache context for every menu link. That makes zero sense for external links, but currently, due to the way the code is structured, that's kind of what needs to happen.Comment #13
drupaldrop CreditAttribution: drupaldrop commentedPatch is rerolled - got a conflict and reuploaded the patch after fix . find details below
First, rewinding head to replay your work on top of it...
Applying: Applying patch from issue 2463753 comment 9845375
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
CONFLICT (content): Merge conflict in core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
Auto-merging core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php
Auto-merging core/modules/menu_ui/src/Tests/MenuTest.php
Auto-merging core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
CONFLICT (content): Merge conflict in core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
Failed to merge in the changes.
Patch failed at 0001 Applying patch from issue 2463753 comment 9845375
Patch is clean now ...
Comment #15
Wim LeersThanks! But looks like we need another one :) You probably missed a small thing.
Comment #16
drupaldrop CreditAttribution: drupaldrop commentedI uploaded an another patch , Hope i donot miss anything this time. Patch is correctly applied to the head though i have not done anything different this time as well just rerolled again.
If it still fails then i might need your help in understanding this where i am going wrong :) .
Comment #17
Wim Leers#16: the patch is now ~90 instead of ~30 KB. That seems wrong :) You had conflicts again, and did not resolve them this time.
In fact, #13 also contains unresolved conflicts. You may want to google "how to resolve git conflicts".
Comment #19
drupaldrop CreditAttribution: drupaldrop commentedThanks Wim Leers for helping me with this reroll - I have uploaded an another updated patch. Hope this time it will pass other wise i will try again :)
Comment #21
Wim LeersGreat work! Now it's 16 KB, and only has 4 failures. That's more like it :)
Your patch includes the entire contents of the previous patch :P :) You might want to fix that :)
There's two levels of pluses there. That seems wrong. Can you open this file locally? You should see that there is one plus at the beginning of many of these lines.
Those are likely the cause of the remaining failures.
Comment #22
drupaldrop CreditAttribution: drupaldrop commentedPatch rerolled again
Comment #24
drupaldrop CreditAttribution: drupaldrop commentedLooking in the testing log under file Drupal\menu_ui\Tests\MenuTest/MenuTest.php . the test is failing at line 989
$this->assertLink(t('Login')); I guess this has to be $this->assertNoLink(t('Login')); to make this test pass becuase it is testing against logged in user
Any thoughts?
Comment #27
StryKaizerWorking on this
Comment #28
StryKaizerPatch reroll.
comment from #10 still applies:
Comment #29
StryKaizerIndention fix
Comment #30
Wim LeersComment #35
iMiksuI'm going to fix the fatal error.
Comment #36
iMiksuDidn't manage to locate the issue, but in the #29 patch, the
\Drupal\Core\Menu\DefaultMenuLinkTreeManipulators::menuLinkCheckAccess()
method it's returningreturn $access_result->cachePerPermissions();
giving this error:I was able to identify that by testing the value returned by following line:
Comment #37
lokapujyaYeah, the login link doesn't show up because the user is logged in.
Comment #38
StryKaizer#37 #24
The issue here is what is said in #10 and still needs to be discussed/addressed.
The administrator SHOULD see the login-link when editting the menu, as he should be able to manage it.
Comment #40
mlncn CreditAttribution: mlncn at Agaric commentedTo be explicit (and to confirm my understanding), this same bug causes a major usability issue if a role is granted both "Link to any page" and "Use the administration pages and help"— users with that role will see links under, say Structure, which they do not have access to, and get access denied when clicking them.
Comment #44
andypostShould be 8.5 first, also mentioned in related #540008: Add a container parameter that can remove the special behavior of UID#1
Comment #46
kalistos CreditAttribution: kalistos at Adyax commentedReroll #29 patch for 8.5.x-dev without tests.
I think it will be not actual soon as #540008: Add a container parameter that can remove the special behavior of UID#1 will be finished.
Comment #49
acbramley CreditAttribution: acbramley at PreviousNext commentedThis is a straight reroll for 8.8.x to see what tests fail (if any). This is a huge UX improvement in removing menu items from the toolbar that shouldn't be there e.g Appearance, Extend, People, Reports. Along with Structure and Configuration for roles without "Use the administration pages and help"
Comment #50
acbramley CreditAttribution: acbramley at PreviousNext commentedHere are before and after screenshots for a user with the following permissions:
Before:
After:
Comment #52
acbramley CreditAttribution: acbramley at PreviousNext commentedThis should fix the first failure.
Comment #55
claudiu.cristeaFixed the tests. And, given that is bug, it should land in
8.8.x
.Comment #56
claudiu.cristeaI've added back the tests removed in #46. I also took a different approach to solve the issue that is mentioned in #5 and #10: Instead of trying to hack into
DefaultMenuLinkTreeManipulators::checkAccess()
, let's use a dedicated manipulator, only for Menu UI. Given that the user that manages the menu form is already granted with permissions to do that, they should be able to see the links but, if the route access is forbidden they will get 403 on trying to access them. This happens only in Menu UI.I've also removed the
user.permissions
cache context from the access resulty checks, as is mentioned in #12.Comment #57
claudiu.cristeaWe have tests now.
Comment #58
claudiu.cristeaI've added some docs in MenuForm, explaining why we're using a dedicated menu tree access check manipulator and removed a cache context from the Menu UI manipulator.
Comment #59
claudiu.cristeaCorrected the docs added in the previous patch.
Comment #60
pfrenssenI think the new approach makes a lot of sense, and addresses the issue raised in #2463753-5: [regression] Do not bypass route access with 'link to any page' permissions for menu links and #2463753-10: [regression] Do not bypass route access with 'link to any page' permissions for menu links in an elegant way. I am not sure though if this still counts as a bug fix since it alters the usage of the "link to any page" permission. This can be discussed, but I think this possibly needs a change record.
This test still needs to have the documentation updated, it still refers to the original implementation that relied on the "link to any page" permission. The following documentation sections need work:
The name of this test is
testCheckAccessWithLinkToAnyPagePermission()
but we are no longer using thelink to any page
permission, so the name of this method should be updated.This test seems to be a bit misleading in that it assigns the
link to any page
permission to the user. This gives the impression that this permission plays a role in the ability to access the login and logout links. This is not the case in the updated implementation. I verified this by removing the line that adds the permission; the test still passes without it. Let's remove thislink to any page
permission from the test to clear up any confusion.Nitpick: remove "a ", since "tree manipulators" is plural.
This is 100% clear, but it feels to me that this is a functional change. This issue is on the borderline between a bug fix and a feature request. Beforehand a user needed the "link to any page" permission to do this, now any user with "administer menu" can handle it.
I don't think there are any security implications (apart from possibly an internal path disclosure), since the pages are still inaccessible, but this possibly needs a change record. TBD.
Very good to see this tested. This 100% guarantees that we will not accidentally introduce a vulnerability here in the future.
Comment #61
swatichouhan012 CreditAttribution: swatichouhan012 at Valuebound for Valuebound commentedi am assigning this issue to me to work on the points mentioned in comment #60.
Comment #62
swatichouhan012 CreditAttribution: swatichouhan012 at Valuebound for Valuebound commentedComment #63
claudiu.cristea@swatichouhan012 your patch cannot be reviewed without an interdiff. See how to create it https://www.drupal.org/documentation/git/interdiff
Comment #64
swatichouhan012 CreditAttribution: swatichouhan012 at Valuebound for Valuebound commented@claudiu.cristea i am adding interdiff for patch. i added patch for point , 1, 2, 3, 4 regarding comment #60.
Comment #66
claudiu.cristea@swatichouhan, the interdiff should show the diffs between your patch (#64) and the previous (#59).
Comment #67
claudiu.cristeaThe interdiff is against #59. Fixed remarks from #60.
Comment #68
pfrenssenThanks @swatichouhan and @claudiu.cristea for the quick response!
It looks good now, I just have one small nitpick left:
I think we should keep the part of the sentence that reads
, hence kept for its cacheability metadata
. This is significant since this is the main reason why we retain links that are inaccessible for certain users. If the cacheability metadata of the inaccessible links were not included, then a user that _has_ the right permissions to access the link would possibly be served a cached version that _doesn't_ include the link.Comment #69
claudiu.cristeaGood point, thank you!
Comment #70
pfrenssenGreat, thanks! This looks good to go.
Comment #71
dwwThis all looks very good and promising. I haven't tested it manually. I don't have time right now to fully comprehend the implications of the changes about cachePerPermissions(), but a quick skim makes initial sense.
Quick drive-by review of the patch. Only minor nits and cosmetic documentation changes, so leaving at RTBC. But if anyone wanted to re-roll to address these, huzzah. Might save an iteration from a core committer saying any of this. ;)
Maybe "Grants access to a menu tree when used in the menu management form." ? There's no checking, it just blindly returns AccessResult::allowed() for everything. ;)
Super tiny nit: missing an if here: "even _if_ the menu"...
Maybe put this NOTE text under @internal? I've seen core committers prefer a comment with @internal declarations, and this note seems to perfectly explain why no one else should use this class.
I love these, but I think I've seen that @see isn't allowed in inline comments. :/ Maybe move a bunch of this comment to the docblock for the whole function?
Comment #72
dhirendra.mishra CreditAttribution: dhirendra.mishra at Valuebound for Valuebound commentedI am doing changes mentioned in above comment.
Comment #73
dhirendra.mishra CreditAttribution: dhirendra.mishra at Valuebound for Valuebound commentedComment #75
pfrenssenThanks! Unfortunately now we have a failure because the Drupal coding standards do not allow to inline
/* ... */
comments.Re the comment #4 from above:
I verified this and it is nowhere mentioned in the API documentation and comment standards that it is not allowed. Even for some other tags (such as "@todo") its use is encouraged. I also found 800+ instances in core where "@see" mentions are used in inline comments so it is definitely a standard pattern.
It is true though that these are not parsed in the autogenerated PHPDoc documentation, but that's not important in this case I suppose.
Anyway, since it is not disallowed let's just put them back.
The "NoPreExistingSchemaTest" failure is also happening in the main dev branch so this can be ignored for now.
Comment #76
dhirendra.mishra CreditAttribution: dhirendra.mishra at Valuebound for Valuebound commentedThanks @pfrenssen
Just updated the patch.Please review.
Comment #78
Hardik_Patel_12 CreditAttribution: Hardik_Patel_12 at QED42 for Drupal India Association commentedRerolling the patch.
Comment #80
mohrerao CreditAttribution: mohrerao as a volunteer and at Srijan | A Material+ Company for Drupal India Association commentedRerolled #76 as #79 did not reroll completely.
Comment #81
claudiu.cristeaInterdiff against #76.
Comment #82
claudiu.cristeaTagging with D8 cacheability as per #12
Comment #83
David Radcliffe CreditAttribution: David Radcliffe as a volunteer commentedThe patch in #81 worked for me prior to updating to Drupal 9.1.4, but now I am unable to apply the patch.
Comment #84
douggreen CreditAttribution: douggreen as a volunteer commentedRerolled
Comment #86
gg24 CreditAttribution: gg24 as a volunteer and at Cheppers commentedRerolled the patch against 9.2.x. Please review.
Comment #87
gg24 CreditAttribution: gg24 as a volunteer and at Cheppers commentedRerolled the patch against 9.2.x. Please review.
Comment #88
gg24 CreditAttribution: gg24 as a volunteer and at Cheppers commentedRerolled the patch against 9.2.x. Please review.
Comment #92
claudiu.cristeaLet's simplify by moving in D10 so that we don't have to deal with so many patch variants.
Comment #94
bob.hinrichs CreditAttribution: bob.hinrichs commentedWhat about Drupal 9.5.0? The most current patch is not applying.
Comment #96
gcbReroll of the patch in 88 for Drupal 9.5.
Comment #98
claudiu.cristea@gcb, D9.5 version is in the last MR https://git.drupalcode.org/project/drupal/-/merge_requests/3118
Comment #100
smustgrave CreditAttribution: smustgrave at Mobomo commentedThis issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.
Reviewing change record - explains the behavior before and how this broke things. The background was good feature explaining
Hiding patches as fixes are being addressed in MR 3118
Removing credit for myself for the rebase to see if tests pass.
The MR is targeted against 9.5.x but did verify it applies cleanly to 10.1.x
Tested this out on Drupal 10.1.x with a standard install
Created a test editor with "administer menu" permission but not "link to any"
As admin user created a menu link under tools menu
Logged into separate browser and verified I don't see that link
Applied fix
In browser with editor I can see the link now but when I click edit I getAccess denied.
Based on the change record I should be able to edit the menu links. If that's not the change then this needs a change record update to be more clear.
Comment #102
DamienMcKennaFWIW with this applied to core the test coverage added in #3271001 passes as expected, that the link to end the visitor's masquerading session only shows when it's supposed to, instead of all the time for user 1.
Comment #103
solideogloria CreditAttribution: solideogloria commentedComment #104
claudiu.cristea@smustgrave, I think the test is self-explanatory
Yes, you should see the link but you should not be able to access it
Comment #105
smustgrave CreditAttribution: smustgrave at Mobomo commentedThen think the CR will need updating.
Also the MR needs to be updated for 11.x
Comment #106
DamienMcKennaThis is the MR in patch format for 11.x; there were a few line offsets but one adjustment for fuzz:
Comment #107
claudiu.cristeaI'm working now to rebase MR on 11.x. Please don't open new patches or MRs
Comment #108
andypostUsually it's faster/easier to squash commits and create new MR or patch
Comment #109
claudiu.cristeaOK, I read once again the CR and it seems pretty clear for me. Is just saying that a menu administrator can manage a menu link even they cannot access the link's underlying route. However, I've added a new phrase that might clear more the explanation. I'm not a native English speaker so, maybe the CR is confusing.
Comment #111
acbramley CreditAttribution: acbramley at PreviousNext commentedTweaked the CR very slightly, otherwise it's reading well!
Comment #112
akalam CreditAttribution: akalam commentedI'm uploading a patch based on the latest version of the MR to ensure it can be applied safety via composer. Tested and working fine in Drupal 10.1.7. Thanks!
Comment #114
acbramley CreditAttribution: acbramley at PreviousNext commentedTrying to help get this across the line - the final fail is
This is due to the Content link not being visible on /admin, debugging through it's because the admin/content route is getting
_access_admin_menu_block_page = TRUE
added, which then gets access denied in SystemAdminMenuBlockAccessCheck::hasAccessToChildMenuItemsI'm not sure if this is expected behaviour and therefore the testAdminPages needs to filter out inaccessible pages, or we need some other modification to get the menu item showing again.
Comment #115
larowlanComment #116
acbramley CreditAttribution: acbramley at PreviousNext commentedUpdated IS a bit.
Comment #117
acbramley CreditAttribution: acbramley at PreviousNext commentedDiscussed with @larowlan, we agreed that the link should not be rendered (this is by design and is the very intention of this issue). Updated test to test for just that.
Comment #118
smustgrave CreditAttribution: smustgrave at Mobomo commentedRan the test-only feature to verify coverage
https://git.drupalcode.org/issue/drupal-2463753/-/jobs/521505 which correctly shows.
From what I can tell open threads are addressed/questions.
All green.
Only concern is if this could land in D10 since it's a permission change, but may just be me and won't let be a blocker.
Comment #119
quietone CreditAttribution: quietone at PreviousNext commentedOh, another old issue, so nice to see these at RTBC.
I read the issue summary and comments here. I did not find any unanswered questions.
I did not #109 where @claudiu.cristea commented that the CR was confusing. I then read the CR and yes, it is confusing to me as a native English speaker. I am tagging for change record updates. I suggest rewording it to use plain English. There is plenty of information online to learn about it. If it were not so late right now I would have a go at improving it.
I left some suggestions on the MR as well. Those are also about the complexity of language in the comments.
I did not do a code review.
Comment #120
claudiu.cristea@quietone, thank you for looking at this.
I’ve applied the suggestions from MR. Regarding the CR, in #109 I didn’t say that the CR is confusing. I said that it might be confusing. For me is not confusing at all but I might be wrong as I’m not a native English speaker. Then, in #111, @acbramley said that he tweaked the CR and it’s ”reading well”. What can I do more, you’re both native English speakers?
I will tentatively set back to RTBC, leaving you (or the core committers) to weight on the CR. I would fix it myself but not clear for me what’s confusing there
Comment #121
acbramley CreditAttribution: acbramley at PreviousNext commentedI've simplified the title of the CR even more, I don't really see how much more rewording is possible unless someone wants to completely rewrite it. It is a somewhat confusing thing to explain though as access related issues are :)
Comment #122
anairamzapHello, the last modifications to the MR include a syntax error on a comment :S
In the core/modules/menu_ui/src/MenuForm.php file line 234
I'll try to fix that now on the MR
Comment #123
claudiu.cristeaThank you, @anairamzap
Comment #124
AndrewTur CreditAttribution: AndrewTur commentedHi, I've been using this patch on a Drupal 9.5.11 version and I cannot apply it anymore.
Comment #125
quietone CreditAttribution: quietone at PreviousNext commentedComment #126
quietone CreditAttribution: quietone at PreviousNext commentedThis was failing spell check so I rebased.
Comment #127
bkosborneOh yes, big +1 to this. I think it should solve #2267603: "Login" link shows up for logged-in users as well.
Comment #130
catchCommitted/pushed to 11.x and cherry-picked to 10.3.x, thanks! This is a big enough change I don't think we should try to cherry-pick/backport to 10.2.x
Comment #131
bkosborneAgreed on keeping this for 10.3
Comment #132
DamienMcKennaIt's fantastic to see this finally committed, thank you all!