From c76bb96da9ca53b70fd3ce5d063722076ffcbcb4 Mon Sep 17 00:00:00 2001 From: MarceauKa Date: Wed, 5 Apr 2017 11:40:44 +0200 Subject: [PATCH] - Implemented Custom SessionGuard - User remember_token is keeped when taking and leaving impersonation - Login and Logout events are not dispatched when impersonating --- src/Guard/SessionGuard.php | 38 ++++++++++++++++++++ src/ImpersonateServiceProvider.php | 52 ++++++++++++++++++++++----- src/Services/ImpersonateManager.php | 31 ++++++---------- tests/EventsTest.php | 56 +++++++++++++++-------------- tests/ImpersonateManagerTest.php | 22 +++++++++++- 5 files changed, 143 insertions(+), 56 deletions(-) create mode 100644 src/Guard/SessionGuard.php diff --git a/src/Guard/SessionGuard.php b/src/Guard/SessionGuard.php new file mode 100644 index 0000000..7dff530 --- /dev/null +++ b/src/Guard/SessionGuard.php @@ -0,0 +1,38 @@ +updateSession($user->getAuthIdentifier()); + + $this->setUser($user); + } + + /** + * Logout the user without updating remember_token + * and without firing the Logout event. + * + * @param void + * @return void + */ + public function quietLogout() + { + $this->clearUserDataFromStorage(); + + $this->user = null; + + $this->loggedOut = true; + } +} diff --git a/src/ImpersonateServiceProvider.php b/src/ImpersonateServiceProvider.php index 8cd76db..b548f98 100644 --- a/src/ImpersonateServiceProvider.php +++ b/src/ImpersonateServiceProvider.php @@ -2,8 +2,10 @@ namespace Lab404\Impersonate; -use Illuminate\Routing\Router; +use Illuminate\Auth\AuthManager; +use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Blade; +use Lab404\Impersonate\Guard\SessionGuard; use Lab404\Impersonate\Middleware\ProtectFromImpersonation; use Lab404\Impersonate\Services\ImpersonateManager; @@ -35,8 +37,7 @@ public function register() $this->app->bind(ImpersonateManager::class, ImpersonateManager::class); - $this->app->singleton(ImpersonateManager::class, function ($app) - { + $this->app->singleton(ImpersonateManager::class, function ($app) { return new ImpersonateManager($app); }); @@ -45,6 +46,7 @@ public function register() $this->registerRoutesMacro(); $this->registerBladeDirectives(); $this->registerMiddleware(); + $this->registerAuthDriver(); } /** @@ -65,19 +67,19 @@ public function boot() */ protected function registerBladeDirectives() { - Blade::directive('impersonating', function() { + Blade::directive('impersonating', function () { return 'check() && app()["auth"]->user()->isImpersonated()): ?>'; }); - Blade::directive('endImpersonating', function() { + Blade::directive('endImpersonating', function () { return ''; }); - Blade::directive('canImpersonate', function() { + Blade::directive('canImpersonate', function () { return 'check() && app()["auth"]->user()->canImpersonate()): ?>'; }); - Blade::directive('endCanImpersonate', function() { + Blade::directive('endCanImpersonate', function () { return ''; }); } @@ -93,8 +95,40 @@ protected function registerRoutesMacro() $router = $this->app['router']; $router->macro('impersonate', function () use ($router) { - $router->get('/impersonate/take/{id}', '\Lab404\Impersonate\Controllers\ImpersonateController@take')->name('impersonate'); - $router->get('/impersonate/leave', '\Lab404\Impersonate\Controllers\ImpersonateController@leave')->name('impersonate.leave'); + $router->get('/impersonate/take/{id}', + '\Lab404\Impersonate\Controllers\ImpersonateController@take')->name('impersonate'); + $router->get('/impersonate/leave', + '\Lab404\Impersonate\Controllers\ImpersonateController@leave')->name('impersonate.leave'); + }); + } + + /** + * @param void + * @return void + */ + protected function registerAuthDriver() + { + /** @var AuthManager $auth */ + $auth = $this->app['auth']; + + $auth->extend('session', function (Application $app, $name, array $config) use ($auth) { + $provider = $auth->createUserProvider($config['provider']); + + $guard = new SessionGuard($name, $provider, $app['session.store']); + + if (method_exists($guard, 'setCookieJar')) { + $guard->setCookieJar($app['cookie']); + } + + if (method_exists($guard, 'setDispatcher')) { + $guard->setDispatcher($app['events']); + } + + if (method_exists($guard, 'setRequest')) { + $guard->setRequest($app->refresh('request', $guard, 'setRequest')); + } + + return $guard; }); } diff --git a/src/Services/ImpersonateManager.php b/src/Services/ImpersonateManager.php index b95f7e9..2fa0ffd 100644 --- a/src/Services/ImpersonateManager.php +++ b/src/Services/ImpersonateManager.php @@ -4,7 +4,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Application; -use Illuminate\Support\Facades\Auth; use Lab404\Impersonate\Events\LeaveImpersonation; use Lab404\Impersonate\Events\TakeImpersonation; @@ -65,23 +64,18 @@ public function getImpersonatorId() */ public function take($from, $to) { - try - { - $impersonator = $this->app['auth']->user(); - $impersonated = $to; - + try { session()->put(config('laravel-impersonate.session_key'), $from->getKey()); - $this->app['auth']->logout(); - $this->app['auth']->login($to); + $this->app['auth']->quietLogout(); + $this->app['auth']->quietLogin($to); - } catch (\Exception $e) - { + } catch (\Exception $e) { unset($e); return false; } - $this->app['events']->fire(new TakeImpersonation($impersonator, $impersonated)); + $this->app['events']->fire(new TakeImpersonation($from, $to)); return true; } @@ -91,19 +85,16 @@ public function take($from, $to) */ public function leave() { - try - { + try { $impersonated = $this->app['auth']->user(); + $impersonator = $this->findUserById($this->getImpersonatorId()); - $this->app['auth']->logout(); - $this->app['auth']->loginUsingId($this->getImpersonatorId()); - - $impersonator = $this->app['auth']->user(); - + $this->app['auth']->quietLogout(); + $this->app['auth']->quietLogin($impersonator); + $this->clear(); - } catch (\Exception $e) - { + } catch (\Exception $e) { unset($e); return false; } diff --git a/tests/EventsTest.php b/tests/EventsTest.php index 89fd856..bd2410b 100644 --- a/tests/EventsTest.php +++ b/tests/EventsTest.php @@ -2,20 +2,15 @@ namespace Lab404\Tests; -use Illuminate\Events\Dispatcher; +use Illuminate\Auth\Events\Login; +use Illuminate\Auth\Events\Logout; +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 { - /** @var ImpersonateManager */ - protected $manager; - - /** @var Dispatcher */ - protected $dispatcher; - /** @var User */ protected $admin; @@ -26,33 +21,42 @@ public function setUp() { parent::setUp(); - $this->manager = $this->app->make(ImpersonateManager::class); - $this->dispatcher = $this->app['events']; $this->admin = User::find(1); $this->user = User::find(2); - - $this->dispatcher->listen(TakeImpersonation::class, function ($impersonator, $impersonated) - { - $this->assertEquals(1, $impersonator->getKey()); - $this->assertEquals(2, $impersonated->getKey()); - }); - - $this->dispatcher->listen(LeaveImpersonation::class, function ($impersonator, $impersonated) - { - $this->assertEquals(1, $impersonator->getKey()); - $this->assertEquals(2, $impersonated->getKey()); - }); } /** @test */ - public function it_dispatch_make_impersonation_event() + public function it_dispatches_events_when_taking_impersonation() { - $this->dispatcher->fire(TakeImpersonation::class, [$this->admin, $this->user]); + Event::fake(); + + $admin = $this->admin; + $user = $this->user; + + $this->assertTrue($admin->impersonate($user)); + + Event::assertDispatched(TakeImpersonation::class, function ($event) use ($admin, $user) { + return $event->impersonator->id == $admin->id && $event->impersonated->id == $user->id; + }); + + Event::assertNotDispatched(Login::class); } /** @test */ - public function it_dispatch_leave_impersonation_event() + public function it_dispatches_events_when_leaving_impersonation() { - $this->dispatcher->fire(LeaveImpersonation::class, [$this->admin, $this->user]); + Event::fake(); + + $admin = $this->admin; + $user = $this->user; + + $this->assertTrue($admin->impersonate($user)); + $this->assertTrue($user->leaveImpersonation()); + + Event::assertDispatched(LeaveImpersonation::class, function ($event) use ($admin, $user) { + return $event->impersonator->id == $admin->id && $event->impersonated->id == $user->id; + }); + + Event::assertNotDispatched(Logout::class); } } diff --git a/tests/ImpersonateManagerTest.php b/tests/ImpersonateManagerTest.php index 4109ef5..cc11574 100644 --- a/tests/ImpersonateManagerTest.php +++ b/tests/ImpersonateManagerTest.php @@ -2,7 +2,6 @@ namespace Lab404\Tests; -use Lab404\Impersonate\Impersonate; use Lab404\Impersonate\Services\ImpersonateManager; use Lab404\Tests\Stubs\Models\User; @@ -76,4 +75,25 @@ public function it_can_leave_impersonating() $this->assertFalse($this->manager->isImpersonating()); $this->assertInstanceOf(User::class, $this->app['auth']->user()); } + + /** @test */ + public function it_keeps_remember_token_when_taking_and_leaving() + { + $admin = $this->manager->findUserById(1); + $admin->remember_token = 'impersonator_token'; + $admin->save(); + + $user = $this->manager->findUserById(2); + $user->remember_token = 'impersonated_token'; + $user->save(); + + $admin->impersonate($user); + $user->leaveImpersonation(); + + $user->fresh(); + $admin->fresh(); + + $this->assertEquals('impersonator_token', $admin->remember_token); + $this->assertEquals('impersonated_token', $user->remember_token); + } }