When logged in as any user with the 'link to any page' permission, the unmasquerade link in the user account menu is visible even if not masquerading. The current method of applying an access check (_user_is_masquerading) to the route works for users that have not been granted the 'link to any page' permission. (@see \Drupal\Core\Menu\DefaultMenuLinkManipulators::menuLinkCheckAccess).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

coderdan created an issue. See original summary.

coderdan’s picture

coderdan’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 2: unmasqeurade_link_transform-2695779-2.patch, failed testing.

The last submitted patch, 2: unmasqeurade_link_transform-2695779-2.patch, failed testing.

andypost’s picture

steveoriol’s picture

Patch #2 works for me on D8.3.2, thanks.

mbovan’s picture

+++ b/src/MasqueradeMenuLinkTree.php
@@ -0,0 +1,31 @@
+    foreach ($tree as $index => $item) {
+      if (strstr($index, 'masquerade.unmasquerade')) {

I think we should check a route name (masquerade.unmasquerade) here rather than the index. This way, we could remove all custom menu links (if there are any) as well.

Providing a patch with the mentioned change.

andypost’s picture

Status: Needs review » Needs work

Nice trick but it needs clean-up and a kind of container param to enable (or somehow other way to configure)

  1. +++ b/src/MasqueradeMenuLinkTree.php
    @@ -0,0 +1,31 @@
    + * @file
    + * Class MasqueradeMenuLinkTree.
    
    +++ b/src/MasqueradeServiceProvider.php
    @@ -0,0 +1,21 @@
    + * @file
    + * MasqueradeServiceProvider class.
    

    @file should be removed

  2. +++ b/src/MasqueradeMenuLinkTree.php
    @@ -0,0 +1,31 @@
    +use Drupal;
    ...
    +        $masquerade = Drupal::service('masquerade');
    

    Use `\Drupal::` as inline

  3. +++ b/src/MasqueradeServiceProvider.php
    @@ -0,0 +1,21 @@
    +    $definition->setClass('Drupal\masquerade\MasqueradeMenuLinkTree');
    

    MasqueradeMenuLinkTree::class

  4. +++ b/src/ProxyClass/MasqueradeMenuLinkTree.php
    @@ -0,0 +1,132 @@
    + * @file
    + * Contains \Drupal\masquerade\ProxyClass\MasqueradeMenuLinkTree.
    ...
    + * This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\masquerade\MasqueradeMenuLinkTree' "modules/contrib/masquerade/src".
    

    Looks it need to be regenerated to remove "@file" header

DuneBL’s picture

I confirm that the patch #10 solve this issue on 8.4
Many thanks!!

abu-zakham’s picture

Status: Needs review » Reviewed & tested by the community

Also I confirm that the patch #10 solve this issue.
Thanks.

andypost’s picture

This approach works but is very hackish cos overrides default core behavior so will not be commited until core decide a way ahead

msajko’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
2.82 KB

Different approach for this issue.

Status: Needs review » Needs work

The last submitted patch, 14: unmasquerade_link-2695779-14.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

msajko’s picture

Status: Needs work » Needs review
FileSize
2.85 KB

Fixed new approach: menu link enabled/disabled

Status: Needs review » Needs work

The last submitted patch, 16: unmasquerade_link-2695779-16.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

imot3k’s picture

This patch fixes the following error:

Exception: Error: Class 'Drupal\masquerade\Plugin\Menu\Drupal' not found
Drupal\masquerade\Plugin\Menu\UnmasqueradeMenuLink::create()() (Line: 46)

wturrell’s picture

(Patch #17 working for me.)

slashrsm’s picture

I can also confirm that the patch works as expected and also the implementation looks sensible.

andypost’s picture

slashrsm’s picture

I think that there is a cache issue with the current patch. To repoduce:

- Make sure caches are enabled etc.
- Log in as the user you will masquerade as later
- "Unmasquerade" link obvioulsy won't appear in the menu, but its disabled state gets cached
- Now log in as another user and masquerade as the first one
- "Unmasquerade" link is not displayed in the menu but it should be
- Now clear the caches and reload the page
- "Unmasquerade" link will appear.

wturrell’s picture

I can reproduce @slashrsm's bug in #22.
Have looked at code, but nothing is jumping out at me.

(Unrelated novice cache context question – given UnmasqueradeMenuLink::getCacheContexts() returns one context only 'session.is_masquerading' - what happens if more than one user is masquerading at the same time?)

andypost’s picture

@wturrell 'session. context prefix means that this link varies per user's session flag

DanChadwick’s picture

The root of this problem is that the menu links don't respect route access for user 1. Personally, I'd say this is a core bug, but I need to work around this in my module for several items that the administrator should not see. Unmasquerade is one of these, since I don't allow users to masquerade as administrators (including user 1).

Using #17 as inspiration, I am simply adding this class in MYMODULE.links.menu.yml to any route that should not be visible to user 1.

/**
 * @file
 * Contains \Drupal\MYMODULE\Plugin\Menu\NoAdminMenuLink.
 */

namespace Drupal\MYMODULE\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkDefault;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountProxyInterface;

/**
 * A menu link that can be hidden from user 1.
 */
class NoAdminMenuLink extends MenuLinkDefault {

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $plugin_definition['enabled'] = $container->get('current_user')->id() != 1;
    return parent::create($container, $configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return ['user.is_super_user'];
  }

}

This would need a little adapting for masquerade. Specifically, the menu should be enabled if uid != 1 || $is_masquerading.

Note that you can continue to use the yml for the title and description, and that there is no reason to override the constructor to save the masquerading status since it is never used. I am not getting caching problems with the context I'm using above and unless uid 1 can masquerade as uid 1 (and I don't think you can masquerade as yourself), I would think that context would work for masquerade.

I'm new to D8, so I may be wrong here. Thanks for the great module. I've been using the D7 version for years.

hudri’s picture

I'm running v2.0@beta, and I can confirm the cache bug of #22 - WITHOUT any patches from here installed.

mglaman’s picture

Assigned: Unassigned » mglaman

Adding a menu link plugin patch, shortly. The problem is you cannot alter the definition in the constructor as it'll become serialized that way.

mglaman’s picture

Assigned: mglaman » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
3.13 KB

Modified a test to show `Unmasquerade` should not show unless you are masquerading.

andypost’s picture

Thank you for patch! Looks nice workaround
Minor nit is missing class description with todo reference to core issue about permission

mglaman’s picture

Status: Needs review » Needs work

👍 thanks, @andypost. I'll make a follow up later. And I will actually run the tests locally and fix the failure.

mglaman’s picture

Okay, so in my testing, locally:

// Permission 'masquerade as super user' granted by default.
$this->assertCanMasqueradeAs($this->rootUser);

`masquerade as super user` is broken, somehow, with this patch.

---

So this patch just uncovered that the tests have been false-positive on masquerading as a root user. The page has "You are not authorized to access this page.", which means that never worked.

---

Edit 2: you always see access denied on /masquerade after submitting the form 🤷‍♂️

mglaman’s picture

Status: Needs work » Needs review
FileSize
1.81 KB

I can't figure out why masquerading as uid1 breaks the menu link. I've tried everything in the tests. Even using messenger::addStatus in isEnabled. It just doesn't even run when switching to uid1.

andypost’s picture

Looks it needs to fix related first

claudiu.cristea’s picture

Status: Needs review » Closed (duplicate)

This is more a workaround. We should focus on the core issue that, magically, fixes also this issue. Closing as duplicate of #2463753: [regression] Do not bypass route access with 'link to any page' permissions for menu links.

steveoriol’s picture

I confirm that patch on "https://www.drupal.org/project/drupal/issues/2463753" fix the issue on D9.3.6