Skip to content

Commit

Permalink
Merge 60f6a6f into 05fc39f
Browse files Browse the repository at this point in the history
  • Loading branch information
Vendin committed Aug 3, 2019
2 parents 05fc39f + 60f6a6f commit 9b961ff
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 35 deletions.
15 changes: 15 additions & 0 deletions config/laravel-impersonate.php
Expand Up @@ -7,6 +7,21 @@
*/
'session_key' => 'impersonated_by',

/**
* The session key used to stored the original user guard.
*/
'session_guard' => 'impersonator_guard',

/**
* The session key used to stored what guard is impersonator using.
*/
'session_guard_using' => 'impersonator_guard_using',

/**
* The default impersonator guard used.
*/
'default_impersonator_guard' => 'web',

/**
* The URI to redirect after taking an impersonation.
*
Expand Down
14 changes: 9 additions & 5 deletions src/Controllers/ImpersonateController.php
Expand Up @@ -24,12 +24,16 @@ public function __construct()

/**
* @param int $id
* @param string|null $guardName
* @throws \Exception
* @return RedirectResponse
*/
public function take(Request $request, $id)
public function take(Request $request, $id, $guardName = null)
{
$guardName = $guardName ?? $this->manager->getDefaultSessionGuard();

// Cannot impersonate yourself
if ($id == $request->user()->getKey()) {
if ($id == $request->user()->getKey() && ($this->manager->getCurrentAuthGuardName() == $guardName)) {
abort(403);
}

Expand All @@ -42,10 +46,10 @@ public function take(Request $request, $id)
abort(403);
}

$user_to_impersonate = $this->manager->findUserById($id);
$userToImpersonate = $this->manager->findUserById($id, $guardName);

if ($user_to_impersonate->canBeImpersonated()) {
if ($this->manager->take($request->user(), $user_to_impersonate)) {
if ($userToImpersonate->canBeImpersonated()) {
if ($this->manager->take($request->user(), $userToImpersonate, $guardName)) {
$takeRedirect = $this->manager->getTakeRedirectTo();
if ($takeRedirect !== 'back') {
return redirect()->to($takeRedirect);
Expand Down
24 changes: 20 additions & 4 deletions src/ImpersonateServiceProvider.php
Expand Up @@ -3,11 +3,14 @@
namespace Lab404\Impersonate;

use Illuminate\Auth\AuthManager;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Foundation\Application;
use Illuminate\View\Compilers\BladeCompiler;
use Lab404\Impersonate\Guard\SessionGuard;
use Lab404\Impersonate\Middleware\ProtectFromImpersonation;
use Lab404\Impersonate\Services\ImpersonateManager;
use Illuminate\Support\Facades\Event;

/**
* Class ServiceProvider
Expand Down Expand Up @@ -57,6 +60,14 @@ public function register()
public function boot()
{
$this->publishConfig();

//We want to remove data from storage on real login and logout
Event::listen(Login::class, function ($event) {
app('impersonate')->clear();
});
Event::listen(Logout::class, function ($event) {
app('impersonate')->clear();
});
}

/**
Expand All @@ -69,15 +80,18 @@ protected function registerBladeDirectives()
{
$this->app->afterResolving('blade.compiler', function (BladeCompiler $bladeCompiler) {
$bladeCompiler->directive('impersonating', function () {
return '<?php if (app()["auth"]->check() && app()["auth"]->user()->isImpersonated()): ?>';
$guard = $this->app['impersonate']->getCurrentAuthGuardName();
return "<?php if (app()['auth']->guard('$guard')->check() && app()['auth']->guard('$guard')->user()->isImpersonated()): ?>";
});

$bladeCompiler->directive('endImpersonating', function () {
return '<?php endif; ?>';
});

$bladeCompiler->directive('canImpersonate', function () {
return '<?php if (app()["auth"]->check() && app()["auth"]->user()->canImpersonate()): ?>';
$guard = $this->app['impersonate']->getCurrentAuthGuardName();
return "<?php if (app()['auth']->guard('$guard')->check()
&& app()['auth']->guard('$guard')->user()->canImpersonate()): ?>";
});

$bladeCompiler->directive('endCanImpersonate', function () {
Expand All @@ -86,8 +100,10 @@ protected function registerBladeDirectives()

$bladeCompiler->directive('canBeImpersonated', function ($expression) {
$user = trim($expression);
$guard = $this->app['impersonate']->getCurrentAuthGuardName();

return "<?php if (app()['auth']->check() && app()['auth']->user()->id != {$user}->id && {$user}->canBeImpersonated()): ?>";
return "<?php if (app()['auth']->guard('$guard')->check()
&& app()['auth']->guard('$guard')->user()->id != {$user}->id && {$user}->canBeImpersonated()): ?>";
});

$bladeCompiler->directive('endCanBeImpersonated', function () {
Expand All @@ -107,7 +123,7 @@ protected function registerRoutesMacro()
$router = $this->app['router'];

$router->macro('impersonate', function () use ($router) {
$router->get('/impersonate/take/{id}',
$router->get('/impersonate/take/{id}/{guardName?}',
'\Lab404\Impersonate\Controllers\ImpersonateController@take')->name('impersonate');
$router->get('/impersonate/leave',
'\Lab404\Impersonate\Controllers\ImpersonateController@leave')->name('impersonate.leave');
Expand Down
5 changes: 3 additions & 2 deletions src/Models/Impersonate.php
Expand Up @@ -33,11 +33,12 @@ public function canBeImpersonated()
* Impersonate the given user.
*
* @param Model $user
* @param string|null $guardName
* @return bool
*/
public function impersonate(Model $user)
public function impersonate(Model $user, $guardName = null)
{
return app(ImpersonateManager::class)->take($this, $user);
return app(ImpersonateManager::class)->take($this, $user, $guardName);
}

/**
Expand Down
90 changes: 79 additions & 11 deletions src/Services/ImpersonateManager.php
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Foundation\Application;
use Lab404\Impersonate\Events\LeaveImpersonation;
use Lab404\Impersonate\Events\TakeImpersonation;
use Exception;

class ImpersonateManager
{
Expand All @@ -27,17 +28,24 @@ public function __construct(Application $app)
/**
* @param int $id
* @return Model
* @throws Exception
*/
public function findUserById($id)
public function findUserById($id, $guardName = null)
{
$model = $this->app['config']->get('auth.providers.users.model');
$userProvider = $this->app['config']->get("auth.guards.$guardName.provider");
$model = $this->app['config']->get("auth.providers.$userProvider.model");

$user = call_user_func([
if(!$model) {
throw new Exception("Auth guard doesn not exist.", 1);
}

/** @var Model $modelInstance */
$modelInstance = call_user_func([
$model,
'findOrFail'
], $id);

return $user;
return $modelInstance;
}

/**
Expand All @@ -57,18 +65,38 @@ public function getImpersonatorId()
return session($this->getSessionKey(), null);
}

/**
* @return string|null
*/
public function getImpersonatorGuardName()
{
return session($this->getSessionGuard(), null);
}

/**
* @return string|null
*/
public function getImpersonatorGuardUsingName()
{
return session($this->getSessionGuardUsing(), null);
}

/**
* @param Model $from
* @param Model $to
* @param string|null $guardName
* @return bool
*/
public function take($from, $to)
public function take($from, $to, $guardName = null)
{
try {
$currentGuard = $this->getCurrentAuthGuardName();
session()->put($this->getSessionKey(), $from->getKey());
session()->put($this->getSessionGuard(), $currentGuard);
session()->put($this->getSessionGuardUsing(), $guardName);

$this->app['auth']->quietLogout();
$this->app['auth']->quietLogin($to);
$this->app['auth']->guard($currentGuard)->quietLogout();
$this->app['auth']->guard($guardName)->quietLogin($to);

} catch (\Exception $e) {
unset($e);
Expand All @@ -86,11 +114,11 @@ public function take($from, $to)
public function leave()
{
try {
$impersonated = $this->app['auth']->user();
$impersonator = $this->findUserById($this->getImpersonatorId());
$impersonated = $this->app['auth']->guard($this->getImpersonatorGuardUsingName())->user();
$impersonator = $this->findUserById($this->getImpersonatorId(), $this->getImpersonatorGuardName());

$this->app['auth']->quietLogout();
$this->app['auth']->quietLogin($impersonator);
$this->app['auth']->guard($this->getCurrentAuthGuardName())->quietLogout();
$this->app['auth']->guard($this->getImpersonatorGuardName())->quietLogin($impersonator);

$this->clear();

Expand All @@ -110,6 +138,8 @@ public function leave()
public function clear()
{
session()->forget($this->getSessionKey());
session()->forget($this->getSessionGuard());
session()->forget($this->getSessionGuardUsing());
}

/**
Expand All @@ -120,6 +150,30 @@ public function getSessionKey()
return config('laravel-impersonate.session_key');
}

/**
* @return string
*/
public function getSessionGuard()
{
return config('laravel-impersonate.session_guard');
}

/**
* @return string
*/
public function getSessionGuardUsing()
{
return config('laravel-impersonate.session_guard_using');
}

/**
* @return string
*/
public function getDefaultSessionGuard()
{
return config('laravel-impersonate.default_impersonator_guard');
}

/**
* @return string
*/
Expand Down Expand Up @@ -147,4 +201,18 @@ public function getLeaveRedirectTo()

return $uri;
}

/**
* @return array
*/
public function getCurrentAuthGuardName()
{
$guards = array_keys(config('auth.guards'));
foreach ($guards as $guard) {
if ($this->app['auth']->guard($guard)->check()) {
return $guard;
}
}
return null;
}
}
36 changes: 34 additions & 2 deletions tests/EventsTest.php
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Support\Facades\Event;
use Lab404\Impersonate\Events\LeaveImpersonation;
use Lab404\Impersonate\Events\TakeImpersonation;
use Lab404\Impersonate\Services\ImpersonateManager;
use Lab404\Tests\Stubs\Models\User;

class EventsTest extends TestCase
Expand All @@ -17,12 +18,16 @@ class EventsTest extends TestCase
/** @var User */
protected $user;

/** @var string */
protected $guard;

public function setUp()
{
parent::setUp();

$this->admin = User::find(1);
$this->user = User::find(2);
$this->guard = 'web';
}

/** @test */
Expand All @@ -33,7 +38,7 @@ public function it_dispatches_events_when_taking_impersonation()
$admin = $this->admin;
$user = $this->user;

$this->assertTrue($admin->impersonate($user));
$this->assertTrue($admin->impersonate($user, $this->guard));

Event::assertDispatched(TakeImpersonation::class, function ($event) use ($admin, $user) {
return $event->impersonator->id == $admin->id && $event->impersonated->id == $user->id;
Expand All @@ -49,8 +54,9 @@ public function it_dispatches_events_when_leaving_impersonation()

$admin = $this->admin;
$user = $this->user;
$this->app['auth']->loginUsingId($admin->id);

$this->assertTrue($admin->impersonate($user));
$this->assertTrue($admin->impersonate($user, $this->guard));
$this->assertTrue($user->leaveImpersonation());

Event::assertDispatched(LeaveImpersonation::class, function ($event) use ($admin, $user) {
Expand All @@ -59,4 +65,30 @@ public function it_dispatches_events_when_leaving_impersonation()

Event::assertNotDispatched(Logout::class);
}

/** @test */
public function it_dispatches_login_event()
{
$manager = $this->app->make(ImpersonateManager::class);
$manager->take($this->admin, $this->user, $this->guard);

event(new Login($this->user, false));

$this->assertFalse($this->app['session']->has($manager->getSessionKey()));
$this->assertFalse($this->app['session']->has($manager->getSessionGuard()));
$this->assertFalse($this->app['session']->has($manager->getSessionGuardUsing()));
}

/** @test */
public function it_dispatches_logout_event()
{
$manager = $this->app->make(ImpersonateManager::class);
$manager->take($this->admin, $this->user, $this->guard);

event(new Logout($this->user));

$this->assertFalse($this->app['session']->has($manager->getSessionKey()));
$this->assertFalse($this->app['session']->has($manager->getSessionGuard()));
$this->assertFalse($this->app['session']->has($manager->getSessionGuardUsing()));
}
}

0 comments on commit 9b961ff

Please sign in to comment.