Skip to content

Commit

Permalink
- Implemented Custom SessionGuard
Browse files Browse the repository at this point in the history
- User remember_token is keeped when taking and leaving impersonation
- Login and Logout events are not dispatched when impersonating
  • Loading branch information
MarceauKa committed Apr 5, 2017
1 parent 68dae79 commit c76bb96
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 56 deletions.
38 changes: 38 additions & 0 deletions src/Guard/SessionGuard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Lab404\Impersonate\Guard;

use Illuminate\Auth\SessionGuard as BaseSessionGuard;
use Illuminate\Contracts\Auth\Authenticatable;

class SessionGuard extends BaseSessionGuard
{
/**
* Log a user into the application without firing the Login event.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function quietLogin(Authenticatable $user)
{
$this->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;
}
}
52 changes: 43 additions & 9 deletions src/ImpersonateServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
});

Expand All @@ -45,6 +46,7 @@ public function register()
$this->registerRoutesMacro();
$this->registerBladeDirectives();
$this->registerMiddleware();
$this->registerAuthDriver();
}

/**
Expand All @@ -65,19 +67,19 @@ public function boot()
*/
protected function registerBladeDirectives()
{
Blade::directive('impersonating', function() {
Blade::directive('impersonating', function () {
return '<?php if (app()["auth"]->check() && app()["auth"]->user()->isImpersonated()): ?>';
});

Blade::directive('endImpersonating', function() {
Blade::directive('endImpersonating', function () {
return '<?php endif; ?>';
});

Blade::directive('canImpersonate', function() {
Blade::directive('canImpersonate', function () {
return '<?php if (app()["auth"]->check() && app()["auth"]->user()->canImpersonate()): ?>';
});

Blade::directive('endCanImpersonate', function() {
Blade::directive('endCanImpersonate', function () {
return '<?php endif; ?>';
});
}
Expand All @@ -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;
});
}

Expand Down
31 changes: 11 additions & 20 deletions src/Services/ImpersonateManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
56 changes: 30 additions & 26 deletions tests/EventsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}
}
22 changes: 21 additions & 1 deletion tests/ImpersonateManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Lab404\Tests;

use Lab404\Impersonate\Impersonate;
use Lab404\Impersonate\Services\ImpersonateManager;
use Lab404\Tests\Stubs\Models\User;

Expand Down Expand Up @@ -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);
}
}

0 comments on commit c76bb96

Please sign in to comment.