From 0b78cbb272391b1d0e58cfef90eb0a4da3192daf Mon Sep 17 00:00:00 2001 From: lfolco Date: Sun, 19 May 2019 16:49:49 -0400 Subject: [PATCH] Invalidate user sessions and handle expired users on login (#22833) --- .../Security/Model/AdminSessionsManager.php | 39 +++++++++++++- .../Magento/User/Cron/DisableExpiredUsers.php | 52 +++++++++++++------ .../Model/ResourceModel/User/Collection.php | 10 ++-- .../User/Cron/DisableExpiredUsersTest.php | 1 + .../ResourceModel/User/CollectionTest.php | 6 +-- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Security/Model/AdminSessionsManager.php b/app/code/Magento/Security/Model/AdminSessionsManager.php index 7503fe04ba480..6dcc4226f5ac8 100644 --- a/app/code/Magento/Security/Model/AdminSessionsManager.php +++ b/app/code/Magento/Security/Model/AdminSessionsManager.php @@ -27,6 +27,11 @@ class AdminSessionsManager */ const LOGOUT_REASON_USER_LOCKED = 10; + /** + * User has been logged out due to an expired user account + */ + const LOGOUT_REASON_USER_EXPIRED = 11; + /** * @var ConfigInterface * @since 100.1.0 @@ -100,7 +105,7 @@ public function __construct( } /** - * Handle all others active sessions according Sharing Account Setting + * Handle all others active sessions according Sharing Account Setting and expired users. * * @return $this * @since 100.1.0 @@ -122,6 +127,11 @@ public function processLogin() } } + if ($this->authSession->getUser()->getExpiresAt()) + { + $this->revokeExpiredAdminUser(); + } + return $this; } @@ -144,6 +154,11 @@ public function processProlong() $this->getCurrentSession()->save(); } + if ($this->authSession->getUser()->getExpiresAt()) + { + $this->revokeExpiredAdminUser(); + } + return $this; } @@ -209,6 +224,11 @@ public function getLogoutReasonMessageByStatus($statusCode) 'Your account is temporarily disabled. Please try again later.' ); break; + case self::LOGOUT_REASON_USER_EXPIRED: + $reasonMessage = __( + 'Your account has expired.' + ); + break; default: $reasonMessage = __('Your current session has been expired.'); break; @@ -353,4 +373,21 @@ private function getIntervalBetweenConsecutiveProlongs() ) ); } + + /** + * Check if the current user is expired and, if so, revoke their admin token. + */ + private function revokeExpiredAdminUser() + { + $expiresAt = $this->dateTime->gmtTimestamp($this->authSession->getUser()->getExpiresAt()); + if ($expiresAt < $this->dateTime->gmtTimestamp()) { + $currentSessions = $this->getSessionsForCurrentUser(); + $currentSessions->setDataToAll('status', self::LOGOUT_REASON_USER_EXPIRED) + ->save(); + $this->authSession->getUser() + ->setIsActive(0) + ->setExpiresAt(null) + ->save(); + } + } } diff --git a/app/code/Magento/User/Cron/DisableExpiredUsers.php b/app/code/Magento/User/Cron/DisableExpiredUsers.php index 8e38814c565f8..312054dacd744 100644 --- a/app/code/Magento/User/Cron/DisableExpiredUsers.php +++ b/app/code/Magento/User/Cron/DisableExpiredUsers.php @@ -6,6 +6,8 @@ namespace Magento\User\Cron; +use Magento\Security\Model\AdminSessionsManager; + /** * Disable expired users. */ @@ -15,33 +17,53 @@ class DisableExpiredUsers /** * @var \Magento\User\Model\ResourceModel\User\CollectionFactory */ - private $collectionFactory; + private $userCollectionFactory; + /** + * @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory + */ + private $adminSessionCollectionFactory; + /** + * @var \Magento\Security\Model\ConfigInterface + */ + private $securityConfig; /** - * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $collectionFactory + * @param \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory + * @param \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionCollectionFactory + * @param \Magento\Security\Model\ConfigInterface $securityConfig */ public function __construct( - \Magento\User\Model\ResourceModel\User\CollectionFactory $collectionFactory + \Magento\User\Model\ResourceModel\User\CollectionFactory $userCollectionFactory, + \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionCollectionFactory, + \Magento\Security\Model\ConfigInterface $securityConfig ) { - $this->collectionFactory = $collectionFactory; + $this->userCollectionFactory = $userCollectionFactory; + $this->adminSessionCollectionFactory = $adminSessionCollectionFactory; + $this->securityConfig = $securityConfig; } /** - * Disable all expired user accounts. - * TODO: add plugin to authentication to disable since not everyone - * has cron running (see \Magento\Security\Model\AdminSessionsManager::processLogin?) + * Disable all expired user accounts and invalidate their sessions. */ public function execute() { - $users = $this->collectionFactory->create() - ->addExpiresAtFilter() - ->addFieldToFilter('is_active', 1) - ; - /** @var \Magento\User\Model\User $user */ - foreach ($users as $user) { - $user->setIsActive(0) - ->setExpiresAt(null) + /** @var \Magento\User\Model\ResourceModel\User\Collection $users */ + $users = $this->userCollectionFactory->create() + ->addActiveExpiredUsersFilter(); + + if ($users->getSize() > 0) + { + /** @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection $currentSessions */ + $currentSessions = $this->adminSessionCollectionFactory->create() + ->addFieldToFilter('user_id', ['in' => $users->getAllIds()]) + ->addFieldToFilter('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_IN) + ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime()); + $currentSessions->setDataToAll('status', AdminSessionsManager::LOGOUT_REASON_USER_EXPIRED) ->save(); } + + $users->setDataToAll('expires_at', null) + ->setDataToAll('is_active', 0) + ->save(); } } diff --git a/app/code/Magento/User/Model/ResourceModel/User/Collection.php b/app/code/Magento/User/Model/ResourceModel/User/Collection.php index 133c253c28574..6b258aa8000bd 100644 --- a/app/code/Magento/User/Model/ResourceModel/User/Collection.php +++ b/app/code/Magento/User/Model/ResourceModel/User/Collection.php @@ -43,18 +43,18 @@ protected function _initSelect() } /** - * Filter users by expires_at. - * @param string|null $now + * Filter for expired, active users. + * @param null $now * @return $this */ - public function addExpiresAtFilter($now = null) + public function addActiveExpiredUsersFilter($now = null) { if ($now === null) { $now = new \DateTime(); $now->format('Y-m-d H:i:s'); } - - $this->addFieldToFilter('expires_at', ['lt' => $now]); + $this->addFieldToFilter('expires_at', ['lt' => $now]) + ->addFieldToFilter('is_active', 1); return $this; } diff --git a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php index 90afe70cf3307..16909086c081f 100644 --- a/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Cron/DisableExpiredUsersTest.php @@ -9,6 +9,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** + * TODO: test logging out sessions * @magentoAppArea adminhtml */ class DisableExpiredUsersTest extends \PHPUnit\Framework\TestCase diff --git a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php index 0a81814c982a8..262468b01fdaf 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/ResourceModel/User/CollectionTest.php @@ -35,9 +35,9 @@ public function testSelectQueryInitialized() /** * @magentoDataFixture Magento/User/_files/expired_users.php */ - public function testExpiresAtFilter() + public function testExpiredActiveUsersFilter() { - $this->collection->addExpiresAtFilter(); - static::assertCount(1, $this->collection); + $this->collection->addActiveExpiredUsersFilter(); + static::assertGreaterThan(1, $this->collection->getSize()); } }