observerConfig = $observerConfig; $this->userResource = $userResource; $this->url = $url; $this->authSession = $authSession; $this->userFactory = $userFactory; $this->encryptor = $encryptor; $this->messageManager = $messageManager; } /** * Admin locking and password hashing upgrade logic implementation * * @param EventObserver $observer * @return void * @throws \Magento\Framework\Exception\LocalizedException */ public function execute(EventObserver $observer) { $password = $observer->getEvent()->getPassword(); /** @var User $user */ $user = $observer->getEvent()->getUser(); $authResult = $observer->getEvent()->getResult(); if (!$authResult && $user->getId()) { // update locking information regardless whether user locked or not $this->_updateLockingInformation($user); } // check whether user is locked $lockExpires = $user->getLockExpires(); if ($lockExpires) { $lockExpires = new \DateTime($lockExpires); if ($lockExpires > new \DateTime()) { throw new UserLockedException( __( 'The account sign-in was incorrect or your account is disabled temporarily. ' . 'Please wait and try again later.' ) ); } } if (!$authResult) { return; } $this->userResource->unlock($user->getId()); $latestPassword = $this->userResource->getLatestPassword($user->getId()); $this->_checkExpiredPassword($latestPassword); if (!$this->encryptor->validateHashVersion($user->getPassword(), true)) { $user->setPassword($password) ->setData('force_new_password', true) ->save(); } } /** * Update locking information for the user * * @param User $user * @return void */ private function _updateLockingInformation($user) { $now = new \DateTime(); $lockThreshold = $this->observerConfig->getAdminLockThreshold(); $maxFailures = $this->observerConfig->getMaxFailures(); if (!($lockThreshold && $maxFailures)) { return; } $failuresNum = (int)$user->getFailuresNum() + 1; /** @noinspection PhpAssignmentInConditionInspection */ if ($firstFailureDate = $user->getFirstFailure()) { $firstFailureDate = new \DateTime($firstFailureDate); } $newFirstFailureDate = false; $updateLockExpires = false; $lockThreshInterval = new \DateInterval('PT' . $lockThreshold.'S'); // set first failure date when this is first failure or last first failure expired if (1 === $failuresNum || !$firstFailureDate || $now->diff($firstFailureDate) > $lockThreshInterval) { $newFirstFailureDate = $now; // otherwise lock user } elseif ($failuresNum >= $maxFailures) { $updateLockExpires = $now->add($lockThreshInterval); } $this->userResource->updateFailure($user, $updateLockExpires, $newFirstFailureDate); } /** * Check whether the latest password is expired * Side-effect can be when passwords were changed with different lifetime configuration settings * * @param array $latestPassword * @return void */ private function _checkExpiredPassword($latestPassword) { if ($latestPassword && $this->observerConfig->_isLatestPasswordExpired($latestPassword)) { if ($this->observerConfig->isPasswordChangeForced()) { $message = __('It\'s time to change your password.'); } else { $myAccountUrl = $this->url->getUrl('adminhtml/system_account/'); $message = __('It\'s time to change your password.', $myAccountUrl); } $messages = $this->messageManager->getMessages(); // Remove existing messages with same ID to avoid duplication $messages->deleteMessageByIdentifier(User::MESSAGE_ID_PASSWORD_EXPIRED); $this->messageManager->addNoticeMessage($message); $message = $messages->getLastAddedMessage(); if ($message) { $message->setIdentifier(User::MESSAGE_ID_PASSWORD_EXPIRED)->setIsSticky(true); $this->authSession->setPciAdminUserIsPasswordExpired(true); } } } }