404labfr / laravel-impersonate

Laravel Impersonate is a plugin that allows you to authenticate as your users.
https://marceau.casals.fr
2.02k stars 204 forks source link

Impersonation URL 403s after impersonate #72

Open mikemike opened 5 years ago

mikemike commented 5 years ago

I'm using latest version of Laravel (5.8) and your package. I've installed using the route macro Route::impersonate() and have the following in my User model:

    /**
     * @return bool
     */
    public function canImpersonate()
    {
        // For example
        return $this->hasRole('admin');
    }

hasRole is part of the very popular Spatie Laravel Persmissions package.

When logged in as an admin, if I visit the route created by your package I'm taken to: /impersonate/take/3

This page 403s. However, when I then visit / I find I'm logged in as the impersonated user (expected behaviour). It looks like the impersonation is potentially being run before middleware or that something else is going on? The route macro is outside of any groups or middleware.

/impersonate/leave works exactly as expected and redirects me correctly.

Any ideas how I can solve this?

HashmatWaziri commented 5 years ago

I am facing the same issue

drbyte commented 5 years ago

In your own debugging, what's causing the 403 response? Where exactly is it happening?

HashmatWaziri commented 5 years ago

Solved:

Add the folllowing in the web.php Route::impersonate();

And then in your blade do sth like this or according to your need: @php $users = \Corals\User\Models\User::all();`

@endphp

@foreach($users as $user)

@canBeImpersonated($user)

<a href="{{ route('impersonate', $user->id) }}">Impersonate this user</a>

@endCanBeImpersonated

@endforeach

I'm using latest version of Laravel (5.8) and your package. I've installed using the route macro Route::impersonate() and have the following in my User model:

    /**
     * @return bool
     */
    public function canImpersonate()
    {
        // For example
        return $this->hasRole('admin');
    }

hasRole is part of the very popular Spatie Laravel Persmissions package.

When logged in as an admin, if I visit the route created by your package I'm taken to: /impersonate/take/3

This page 403s. However, when I then visit / I find I'm logged in as the impersonated user (expected behaviour). It looks like the impersonation is potentially being run before middleware or that something else is going on? The route macro is outside of any groups or middleware.

/impersonate/leave works exactly as expected and redirects me correctly.

Any ideas how I can solve this?

MarceauKa commented 5 years ago

Do you use the web guard?

jgcbrouns commented 4 years ago

I have the same issue. One cannot double impersonate (impersonate another user when already impersonating). What could solve this is a @isNotImpersonating directive. You can then show the "impersonate" button only when you are not impersonating already

jgcbrouns commented 4 years ago

So I am doing the following:

Added the inverse blade directive of @isImpersonating:

AppServiceProvider.php:

    public function boot()
    {
         \Blade::directive('isNotImpersonating', function () {
            return "<?php if (!is_impersonating()) : ?>";
        });
        \Blade::directive('endIsNotImpersonating', function () {
            return '<?php endif; ?>';
        });
    }

Then in your blade template you can use:

@isNotImpersonating
<a class="dropdown-item"
href="{{ route('impersonate', $resellerUser->id) }}">
Impersonate
</a>
@endIsNotImpersonating

Hence not showing the button when already impersonating and therefore not double impersonating (which gives the 403 error).

drbyte commented 4 years ago

I've not found any need for another Blade directive.

I implement it like the following. The @canImpersonate checks for role authorization. @canBeImpersonated verifies that the indicated user is allowed to be impersonated. The criteria for these are in the User model, also shown below.

I think simply doing the canBeImpersonated() method override is enough to prevent the double-impersonation you mention, and also allows you control over additional criteria you may wish to incorporate.

User "show" template:

    @canImpersonate
      @canBeImpersonated($user)
    <div class="btn-group float-right d-print-none" role="group" aria-label="Impersonate">
        <a href="{{ route('impersonate', $user->id) }}"><button type="button" class="btn btn-success text-center"><i class="fa fa-user-circle fa-lg" title="Impersonate this user"></i></button></a>
    </div>
      @endCanBeImpersonated
    @endCanImpersonate

Nav bar next to Logout:

    @impersonating
        <a class="bg-danger dropdown-item" href="{{ route('impersonate.leave') }}" data-shortcut="leave"><i class="fa fa-btn fa-times-circle" aria-hidden="true"></i>Leave Impersonation</a>
    @endImpersonating

User model methods which override the Trait defaults:

    /**
     * Return true or false whether the user can impersonate another user.
     *
     */
    public function canImpersonate(): bool
    {
        return $this->hasRole('Admin') || $this->hasRole('Super-Admin');
    }

    /**
     * Return boolean whether the user can be impersonated.
     * Here we deny impersonation of oneself (or double-impersonating) as that would be pointless.
     */
    public function canBeImpersonated(): bool
    {
        return $this->id != Auth::id();
    }