diff --git a/Module.php b/Module.php index 21a0ea0f3..70a6d7a44 100644 --- a/Module.php +++ b/Module.php @@ -57,6 +57,12 @@ class Module extends BaseModule /** @var bool Enable the 'impersonate as another user' function */ public $enableImpersonateUser = true; + /** @var array which roles are allowed to impersonate into another user? */ + public $allowImpersonateForRoles = ['admin']; + + /** @var array which roles are never allowed to be impersonated into? */ + public $denyImpersonateIntoRoles = ['admin']; + /** @var int Email changing strategy. */ public $emailChangeStrategy = self::STRATEGY_DEFAULT; diff --git a/controllers/AdminController.php b/controllers/AdminController.php index 18547a671..1f414610c 100644 --- a/controllers/AdminController.php +++ b/controllers/AdminController.php @@ -332,20 +332,17 @@ public function actionInfo($id) */ public function actionSwitch($id = null) { - if (!$this->module->enableImpersonateUser) { - throw new ForbiddenHttpException(Yii::t('user', 'Impersonate user is disabled in the application configuration')); - } - - if(!$id && Yii::$app->session->has(self::ORIGINAL_USER_SESSION_KEY)) { + if (!$id && Yii::$app->session->has(self::ORIGINAL_USER_SESSION_KEY)) { // Logout of impersonation $user = $this->findModel(Yii::$app->session->get(self::ORIGINAL_USER_SESSION_KEY)); Yii::$app->session->remove(self::ORIGINAL_USER_SESSION_KEY); } else { - if (!Yii::$app->user->identity->isAdmin) { + $user = $this->findModel($id); + + if (!self::impersonationIsAllowed(Yii::$app->user, $user)) { throw new ForbiddenHttpException; } - $user = $this->findModel($id); Yii::$app->session->set(self::ORIGINAL_USER_SESSION_KEY, Yii::$app->user->id); } @@ -360,6 +357,43 @@ public function actionSwitch($id = null) return $this->goHome(); } + /** + * The impersonation is only allowed for all roles in $allowImpersonationForRoles and + * is always denied if the target user has one of the roles listed in $denyImpersonationIntoRoles. + * + * It is also denied if we are already impersonated into someone else. + * + * @param $user the user that wants to impersonate (usually Yii::$app->user) + * @param $target_user the target user that we want to impersonate into + * @return bool + */ + public static function impersonationIsAllowed($user, $target_user) + { + $module = Yii::$app->getModule('user'); + + if (!$module->enableImpersonateUser + || Yii::$app->session->has(self::ORIGINAL_USER_SESSION_KEY) + || $user->id == $target_user->id) { + return false; + } + + $auth = Yii::$app->authManager; + + foreach (Yii::$app->getModule('user')->denyImpersonateIntoRoles as $denied_role) { + if ($auth->checkAccess($target_user->id, $denied_role)) { + return false; + } + } + + foreach (Yii::$app->getModule('user')->allowImpersonateForRoles as $allowed_role) { + if ($user->can($allowed_role)) { + return true; + } + } + + return false; + } + /** * If "dektrium/yii2-rbac" extension is installed, this page displays form * where user can assign multiple auth items to user. diff --git a/views/admin/index.php b/views/admin/index.php index 90fc9575c..a9e798030 100644 --- a/views/admin/index.php +++ b/views/admin/index.php @@ -117,7 +117,7 @@ 'template' => '{switch} {resend_password} {update} {delete}', 'buttons' => [ 'resend_password' => function ($url, $model, $key) { - if (\Yii::$app->user->identity->isAdmin && !$model->isAdmin) { + if (Yii::$app->user->identity->isAdmin && !$model->isAdmin) { return ' @@ -125,7 +125,7 @@ } }, 'switch' => function ($url, $model) { - if(\Yii::$app->user->identity->isAdmin && $model->id != Yii::$app->user->id && Yii::$app->getModule('user')->enableImpersonateUser) { + if (AdminController::impersonationIsAllowed(Yii::$app->user, $model)) { return Html::a('', ['/user/admin/switch', 'id' => $model->id], [ 'title' => Yii::t('user', 'Become this user'), 'data-confirm' => Yii::t('user', 'Are you sure you want to switch to this user for the rest of this Session?'),