From d3b4161f7cd73d2681d1099e65529eeee48a6086 Mon Sep 17 00:00:00 2001 From: Antonio Ramirez Date: Tue, 18 Jul 2017 23:47:23 +0200 Subject: [PATCH] re #22 added impersonation --- src/User/Bootstrap.php | 1 + src/User/Controller/AdminController.php | 17 ++++++ src/User/Event/UserEvent.php | 2 + src/User/Module.php | 13 ++-- src/User/Service/SwitchIdentityService.php | 69 ++++++++++++++++++++++ src/User/resources/views/admin/index.php | 25 +++++++- 6 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 src/User/Service/SwitchIdentityService.php diff --git a/src/User/Bootstrap.php b/src/User/Bootstrap.php index 76d6e42..787993b 100644 --- a/src/User/Bootstrap.php +++ b/src/User/Bootstrap.php @@ -100,6 +100,7 @@ class Bootstrap implements BootstrapInterface $di->set(Service\UserConfirmationService::class); $di->set(Service\AuthItemEditionService::class); $di->set(Service\UpdateAuthAssignmentsService::class); + $di->set(Service\SwitchIdentityService::class); // email change strategy $di->set(Strategy\DefaultEmailChangeStrategy::class); diff --git a/src/User/Controller/AdminController.php b/src/User/Controller/AdminController.php index 90343b4..a911556 100644 --- a/src/User/Controller/AdminController.php +++ b/src/User/Controller/AdminController.php @@ -18,6 +18,7 @@ use Da\User\Model\Profile; use Da\User\Model\User; use Da\User\Query\UserQuery; use Da\User\Search\UserSearch; +use Da\User\Service\SwitchIdentityService; use Da\User\Service\UserBlockService; use Da\User\Service\UserConfirmationService; use Da\User\Service\UserCreateService; @@ -80,6 +81,7 @@ class AdminController extends Controller 'delete' => ['post'], 'confirm' => ['post'], 'block' => ['post'], + 'switch-identity' => ['post'] ], ], 'access' => [ @@ -287,4 +289,19 @@ class AdminController extends Controller return $this->redirect(Url::previous('actions-redirect')); } + + public function actionSwitchIdentity($id = null) + { + /** @var \Da\User\Module $module */ + $module = $this->module; + if (false === $module->enableSwitchIdentities) { + Yii::$app->getSession()->setFlash('danger', Yii::t('usuario', 'Switch identities is disabled.')); + + return $this->redirect(['index']); + } + + $this->make(SwitchIdentityService::class, [$this, 'userId' => $id])->run(); + + return $this->goHome(); + } } diff --git a/src/User/Event/UserEvent.php b/src/User/Event/UserEvent.php index aa429f1..d8f032e 100644 --- a/src/User/Event/UserEvent.php +++ b/src/User/Event/UserEvent.php @@ -34,6 +34,8 @@ class UserEvent extends Event const EVENT_AFTER_BLOCK = 'afterBlock'; const EVENT_BEFORE_LOGOUT = 'beforeLogout'; const EVENT_AFTER_LOGOUT = 'afterLogout'; + const EVENT_BEFORE_SWITCH_IDENTITY = 'beforeSwitchIdentity'; + const EVENT_AFTER_SWITCH_IDENTITY = 'afterSwitchIdentity'; protected $user; diff --git a/src/User/Module.php b/src/User/Module.php index 853e83a..72b2dd1 100644 --- a/src/User/Module.php +++ b/src/User/Module.php @@ -31,6 +31,10 @@ class Module extends BaseModule * @var bool whether to display flash messages or not */ public $enableFlashMessages = true; + /** + * @var bool whether to be able to, as an admin, impersonate other users + */ + public $enableSwitchIdentities = true; /** * @var bool whether to generate passwords automatically and remove the password field from the registration form */ @@ -92,7 +96,6 @@ class Module extends BaseModule * @see Bootstrap::buildClassMap() for more details */ public $classMap = []; - /** * @var array the url rules (routes) */ @@ -102,12 +105,14 @@ class Module extends BaseModule '' => 'registration/', 'confirm//' => 'registration/confirm', 'forgot' => 'recovery/request', - 'recover//' => 'recovery/reset', - 'settings/' => 'settings/', + 'recover//' => 'recovery/reset' ]; - /** * @var string */ public $viewPath = '@Da/User/resources/views'; + /** + * @var string the session key name to impersonate users + */ + public $switchIdentitySessionKey = 'yuik_user'; } diff --git a/src/User/Service/SwitchIdentityService.php b/src/User/Service/SwitchIdentityService.php new file mode 100644 index 0000000..dfa538c --- /dev/null +++ b/src/User/Service/SwitchIdentityService.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Da\User\Service; + +use Da\User\Contracts\ServiceInterface; +use Da\User\Controller\AdminController; +use Da\User\Event\UserEvent; +use Da\User\Model\User; +use Da\User\Module; +use Da\User\Query\UserQuery; +use Da\User\Traits\ContainerAwareTrait; +use Yii; +use yii\web\ForbiddenHttpException; +use yii\web\IdentityInterface; + +class SwitchIdentityService implements ServiceInterface +{ + use ContainerAwareTrait; + + protected $controller; + protected $switchIdentitySessionKey; + protected $userId; + protected $userQuery; + + public function __construct(AdminController $controller, $userId, UserQuery $userQuery) + { + /** @var Module $module */ + $module = $controller->module; + $this->controller = $controller; + $this->switchIdentitySessionKey = $module->switchIdentitySessionKey; + $this->userId = $userId; + $this->userQuery = $userQuery; + } + + public function run() + { + $session = Yii::$app->session; + if (null === $this->userId) { // switch back identities + $user = $this->userQuery->whereId($session->get($this->switchIdentitySessionKey))->one(); + $session->remove($this->switchIdentitySessionKey); + } else { + /** @var User $identity */ + $identity = Yii::$app->user->identity; + if (!$identity->getIsAdmin()) { + // Only admins allowed on module. Developers can override the service and implement different + // approach. For example, by roles other than, and including, admin. + throw new ForbiddenHttpException(); + } + $user = $this->userQuery->whereId($this->userId)->one(); + $session->set($this->switchIdentitySessionKey, $this->userId); + } + + $event = $this->make(UserEvent::class, [$user]); + + $this->controller->trigger(UserEvent::EVENT_BEFORE_SWITCH_IDENTITY, $event); + /** @var IdentityInterface $user */ + Yii::$app->user->switchIdentity($user, $session->timeout); + $this->controller->trigger(UserEvent::EVENT_AFTER_SWITCH_IDENTITY, $event); + } +} diff --git a/src/User/resources/views/admin/index.php b/src/User/resources/views/admin/index.php index 7ab015a..bb4dd17 100644 --- a/src/User/resources/views/admin/index.php +++ b/src/User/resources/views/admin/index.php @@ -18,10 +18,13 @@ use yii\widgets\Pjax; * @var $this yii\web\View * @var $dataProvider yii\data\ActiveDataProvider * @var $searchModel Da\User\Search\UserSearch + * @var $module Da\User\Module */ $this->title = Yii::t('usuario', 'Manage users'); $this->params['breadcrumbs'][] = $this->title; + +$module = Yii::$app->getModule('user'); ?> beginContent('@Da/User/resources/views/shared/admin_layout.php') ?> @@ -106,7 +109,27 @@ $this->params['breadcrumbs'][] = $this->title; ], [ 'class' => 'yii\grid\ActionColumn', - 'template' => '{update} {delete}', + 'template' => '{switch} {update} {delete}', + 'buttons' => [ + 'switch' => function ($url, $model) use ($module) { + if ($model->id != Yii::$app->user->id && $module->enableSwitchIdentities) { + return Html::a( + '', + ['/user/admin/switch-identity', 'id' => $model->id], + [ + 'title' => Yii::t('usuario', 'Impersonate this user'), + 'data-confirm' => Yii::t( + 'usuario', + 'Are you sure you want to switch to this user for the rest of this Session?' + ), + 'data-method' => 'POST', + ] + ); + } + + return null; + } + ] ], ], ]