johnbillion / user-switching

WordPress plugin that provides instant switching between user accounts.
https://wordpress.org/plugins/user-switching/
GNU General Public License v2.0
187 stars 49 forks source link

Allow switching to a specific User ID(s) only #95

Closed fuad-tareq closed 1 year ago

fuad-tareq commented 1 year ago

I love the fact that we can do this... however, I've recently come up with a new use case that I'm not sure is supported yet.

I'd like to allow some users to be able to switch to a specific user only. So while the ability to grant the switch_users capability can be granted to some users, that will only get me half-way to my goal. In a nutshell, I'd like to give some users the ability to switch_users but I don't want them to be able to use that capability later and switch to some other users, I want to only allow them to switch to a certain User ID. Is this possible?

In the example in your FAQ, we have access to the current user's ID that's attempting to switch users but it doesn't seem like we have access to the User ID of the user they're attempting to switch to which would have made my request possible.

I also cannot pass that variable through an anonymous filter function because I am getting it from an AJAX handler. EDIT: Well I'll be damned! Turns out my AJAX handler was never executing because I had named it switch_to_user hahahaha. Ignore me then, the anonymous filter works just fine in the AJAX handler.

fuad-tareq commented 1 year ago

Actually, I've come up with another issue regarding this task. I've been attempting the following when generating the User Switching link:

add_filter('user_has_cap', function($allcaps, $caps, $args, $user) use($switchToUser) {
    if ('switch_to_user' === $args[0]) {
        if (36 === $switchToUser->ID)
            $allcaps['switch_users'] = true;
     }
    return $allcaps;
}, 9, 4);

You might already see why this is going to fail... the user switching link is generated fine. But when clicking on that link, I get a 403 Could not switch users. I've been inspecting the plugin's code base to debug the issue and if I understand correctly, it seems that the plugin expects "the user being switched to" to also have the switch_to_user capability. I suspect this is needed to be able to allow that user to switch back to the original user? If I'm correct here, then I think there's a potential use-case here to automatically give a "switch back to old user" permission to that user without us having to add that manually ourselves.

In the meantime, my code snippet above is running inside an AJAX handler to generate a link that only allows the current user to switch to a certain user ID. I suspect I'm missing something else here that would allow "the user that has been switched to" to have this capability as well?

EDIT:

It is worth mentioning that none of these users are admins and that is why I'm adding these temporary permissions. I've also tried the following snippet in place of the above snippet:

add_filter('user_has_cap', function($allcaps, $caps, $args, $user) use($switchToUser) {
    if ('switch_to_user' === $args[0]) {
        if (36 === $switchToUser->ID) {
            $allcaps['switch_users'] = true;
            // Also add this capability to the switched user in order to allow them to switch back.
            $switchToUser->add_cap('switch_users');
        }
     }
    return $allcaps;
}, 9, 4);

I thought for sure the above snippet would do the trick... but it seems the current_user_can( 'switch_to_user', $user_id ) check in user-switching.php is still failing. There must be another reason then?

johnbillion commented 1 year ago

The capabilities of the target user aren't a consideration. What function are you using for the link? The user_switching::maybe_switch_url() function is the best for this as it returns boolean false if the current user can't switch.

fuad-tareq commented 1 year ago

Thank you for the quick response. That is indeed the one I'm using and it returns the link but clicking that link seems to return that 403 error mentioned above. I've also updated my comment above to add some more information.

johnbillion commented 1 year ago

Not sure what the problem could be then. I would debug this by getting rid of the Ajax complication for now and outputting the link directly to the page, then go from there.

fuad-tareq commented 1 year ago

Just to be clear, are you saying there should not be a 403 error after the link is generated successfully? Because I'm getting the link just fine but my 403 comes after attempting to go to that link, this is the link I'm currently getting (I'm adding a redirect_to variable to it): https://site.com/wp-login.php?action=switch_to_user&user_id=36&nr=1&_wpnonce=8bfb6a96f3&redirect_to=https://site.com/dashboard-member

Going to that link gives me the 403 Could not switch users..

Update:

Upon further inspecting the user-switching.php code, I've found where you call the add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );. Unsurprisingly, it seems that filter is being executed 3 times after the link above is clicked. The first time this happens, the $user_caps correctly has the required switch_users capability, but the second and third times it does not. There seems to be at least 1 redirection causing this and most likely is no fault of your plugin. I'm investigating further.

fuad-tareq commented 1 year ago

Well I'm not really sure I fully understand this but the issue seems to be resolved by simply taking the filter override snippet (I posted above) outside of the AJAX handler and placing it somewhere like when WordPress initializes.

My guess is that your plugin needs to be able to access that filter (to authorize the current user) twice, once when the user_switching::maybe_switch_url() is called (inside the AJAX handler) to generate the link, and the second time is after the link is actually clicked. By placing my filter override snippet from above inside the AJAX handler, it meant that your plugin was only able to access the filter override once (during link creation) and not the second time after the link is actually clicked (and WordPress is reloaded).