From 6439a83edbdb9f74de4607bf9173e4a638ae6e33 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 17:34:18 +0200 Subject: [PATCH 1/8] app/listeners/ActivityLog.php: Adding onImpersonate listener --- app/listeners/ActivityLog.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/listeners/ActivityLog.php b/app/listeners/ActivityLog.php index 906b3be..4f4787c 100644 --- a/app/listeners/ActivityLog.php +++ b/app/listeners/ActivityLog.php @@ -23,6 +23,19 @@ class ActivityLog extends Injectable $this->_log($auth->getUser(), sprintf("Logged in (%s)", $type)); } + /** + * On Impersonate event. + * + * @param Event $event + * @param Auth $auth + * @param User $user The user Impersonating the user in $auth + */ + public function onImpersonate(Event $event, Auth $auth, User $user) + { + $imp = $auth->getUser(); + $this->_log($user, sprintf("Impersonated user (%s:%s)", $imp->getId(), $imp->getUsername())); + } + /** * @param Event $event * @param User $auth From 69fd7a6e193e6132c9dc88d45ef5c44b8f92957d Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 17:34:59 +0200 Subject: [PATCH 2/8] app/library/Auth.php: adding impersonate() and impersonateClear() methods --- app/library/Auth.php | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/app/library/Auth.php b/app/library/Auth.php index 81750da..33a46f4 100644 --- a/app/library/Auth.php +++ b/app/library/Auth.php @@ -10,6 +10,7 @@ use App\Model\Data\User, class Auth extends Injectable { const SESSION_KEY = 'auth'; + const IMPERSONATEOR_ID = 'auth.impersonateor'; /** * Login using email/user + password combination. @@ -86,6 +87,34 @@ class Auth extends Injectable $this->eventsManager->fire('auth:onLogin', $this, 'System'); } + /** + * Impersonate a user + * + * @param User $user + */ + public function impersonate(User $user) + { + $current = $this->getIdentity(); + if ($current === null) { + throw new \InvalidArgumentException("Need to be authenticated to be able to impersonate someone"); + } + + if ($current->getId() === $user->getId()) { + // Same user + throw new \DomainException("Can't impersonate yourself"); + } + + $this->session->set(self::IMPERSONATEOR_ID, $current->getId()); + $this->setIdentity($user->getId()); + $this->eventsManager->fire('auth:onImpersonate', $this, $current); + } + + public function impersonateClear($imp_id) + { + $this->session->remove(self::IMPERSONATEOR_ID); + $this->session->set(self::SESSION_KEY, $imp_id); + } + /** * @param $identity * @return Auth @@ -135,7 +164,12 @@ class Auth extends Injectable */ public function clearIdentity() { - $this->session->remove(self::SESSION_KEY); + $imp_id = $this->session->get(self::IMPERSONATEOR_ID); + if ($imp_id !== null) { + $this->impersonateClear($imp_id); + } else { + $this->session->remove(self::SESSION_KEY); + } return $this; } } From 4ac770f7339491e5a28708843b75bef6a6390bdb Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 17:49:36 +0200 Subject: [PATCH 3/8] app/controllers/backend/UserController.php: adding impersonateAction() --- app/controllers/backend/UserController.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/controllers/backend/UserController.php b/app/controllers/backend/UserController.php index 8737728..bad595e 100644 --- a/app/controllers/backend/UserController.php +++ b/app/controllers/backend/UserController.php @@ -113,4 +113,17 @@ class UserController extends \Phalcon\Mvc\Controller } $this->response->redirect('/admin'); } + + public function impersonateAction($id) + { + $user = User::findFirstById($id); + + try { + $this->auth->impersonate($user); + $this->response->redirect('/'); + } catch (\Exception $ex) { + $this->flash->error($ex->getMessage()); + $this->response->redirect('/admin'); + } + } } From 24edb5cb5986944154441e1380e437b65886ac14 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 17:50:21 +0200 Subject: [PATCH 4/8] app/config/routes.yml: add impersonate route. --- app/config/routes.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/config/routes.yml b/app/config/routes.yml index 8974a32..2e905c6 100644 --- a/app/config/routes.yml +++ b/app/config/routes.yml @@ -73,6 +73,9 @@ router: backend-user-edit: pattern: '/admin/user/{id:([0-9]+)}' path: backend::user::edit + backend-user-impersonate: + pattern: '/admin/impersonate/{id:([0-9]+)}' + path: backend::user::impersonate backend-user-activation-email: pattern: '/admin/user/{id:([0-9]+)}/activation' path: backend::user::activation-email From 0634db6d0c92e5f88794fc12b4926f96802d5910 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 17:50:36 +0200 Subject: [PATCH 5/8] app/views/backend/user/index.volt: Add icon link to impersonate users --- app/views/backend/user/index.volt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/backend/user/index.volt b/app/views/backend/user/index.volt index 821fca9..94b5241 100644 --- a/app/views/backend/user/index.volt +++ b/app/views/backend/user/index.volt @@ -20,6 +20,7 @@ Email Type Status +   @@ -37,6 +38,11 @@ {{ item.email }} {{ item.type | capitalize }} {{ item.status }} + + + {{ icon('solid/user-secret') }} + + {% endfor %} From 6dc1d0fb872c644d856b6a0243dbfa7ca3cd404c Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 18:03:23 +0200 Subject: [PATCH 6/8] app/library/Auth.php: Fix typo. --- app/library/Auth.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/library/Auth.php b/app/library/Auth.php index 33a46f4..7d343cb 100644 --- a/app/library/Auth.php +++ b/app/library/Auth.php @@ -10,7 +10,7 @@ use App\Model\Data\User, class Auth extends Injectable { const SESSION_KEY = 'auth'; - const IMPERSONATEOR_ID = 'auth.impersonateor'; + const IMPERSONATOR_ID = 'auth.impersonator'; /** * Login using email/user + password combination. @@ -104,14 +104,14 @@ class Auth extends Injectable throw new \DomainException("Can't impersonate yourself"); } - $this->session->set(self::IMPERSONATEOR_ID, $current->getId()); + $this->session->set(self::IMPERSONATOR_ID, $current->getId()); $this->setIdentity($user->getId()); $this->eventsManager->fire('auth:onImpersonate', $this, $current); } public function impersonateClear($imp_id) { - $this->session->remove(self::IMPERSONATEOR_ID); + $this->session->remove(self::IMPERSONATOR_ID); $this->session->set(self::SESSION_KEY, $imp_id); } @@ -164,7 +164,7 @@ class Auth extends Injectable */ public function clearIdentity() { - $imp_id = $this->session->get(self::IMPERSONATEOR_ID); + $imp_id = $this->session->get(self::IMPERSONATOR_ID); if ($imp_id !== null) { $this->impersonateClear($imp_id); } else { From bf940f95231d82eb72c2ed0dee8014c3bb254f6a Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 18:04:35 +0200 Subject: [PATCH 7/8] app/library/Auth.php: Adding getImpersonator() --- app/library/Auth.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/library/Auth.php b/app/library/Auth.php index 7d343cb..80ea7bd 100644 --- a/app/library/Auth.php +++ b/app/library/Auth.php @@ -87,6 +87,12 @@ class Auth extends Injectable $this->eventsManager->fire('auth:onLogin', $this, 'System'); } + public function getImpersonator() + { + $id = $this->session->get(self::IMPERSONATOR_ID); + return $id !== null ? User::findFirst($id) : null; + } + /** * Impersonate a user * From a757e9527799e197c72577c52af1555418c44a75 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sun, 30 Apr 2023 18:05:27 +0200 Subject: [PATCH 8/8] app/views/_common/_components/navigation.volt: show both users if the user are impersonating another user. --- app/views/_common/_components/navigation.volt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/_common/_components/navigation.volt b/app/views/_common/_components/navigation.volt index eaf3fd7..9b11888 100644 --- a/app/views/_common/_components/navigation.volt +++ b/app/views/_common/_components/navigation.volt @@ -12,6 +12,8 @@ data-bs-toggle="dropdown" role="button" aria-expanded="false"> {{ icon('solid/user') }} {{ auth.getUser().username }} + {% set imp = auth.getImpersonator() %} + {% if imp %}( {{ icon('solid/user-secret') }} {{ imp.username }} ){% endif %}