Authentication.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Customer\Model;
  7. use Magento\Customer\Api\CustomerRepositoryInterface;
  8. use Magento\Customer\Model\ResourceModel\CustomerRepository;
  9. use Magento\Customer\Model\CustomerAuthUpdate;
  10. use Magento\Backend\App\ConfigInterface;
  11. use Magento\Framework\Encryption\EncryptorInterface as Encryptor;
  12. use Magento\Framework\Exception\InvalidEmailOrPasswordException;
  13. use Magento\Framework\Exception\State\UserLockedException;
  14. /**
  15. * Class Authentication
  16. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  17. */
  18. class Authentication implements AuthenticationInterface
  19. {
  20. /**
  21. * Configuration path to customer lockout threshold
  22. */
  23. const LOCKOUT_THRESHOLD_PATH = 'customer/password/lockout_threshold';
  24. /**
  25. * Configuration path to customer max login failures number
  26. */
  27. const MAX_FAILURES_PATH = 'customer/password/lockout_failures';
  28. /**
  29. * @var CustomerRegistry
  30. */
  31. protected $customerRegistry;
  32. /**
  33. * Backend configuration interface
  34. *
  35. * @var \Magento\Backend\App\ConfigInterface
  36. */
  37. protected $backendConfig;
  38. /**
  39. * @var \Magento\Framework\Stdlib\DateTime
  40. */
  41. protected $dateTime;
  42. /**
  43. * @var Encryptor
  44. */
  45. protected $encryptor;
  46. /**
  47. * @var CustomerRepositoryInterface
  48. */
  49. protected $customerRepository;
  50. /**
  51. * @var CustomerAuthUpdate
  52. */
  53. private $customerAuthUpdate;
  54. /**
  55. * @param CustomerRepositoryInterface $customerRepository
  56. * @param CustomerRegistry $customerRegistry
  57. * @param ConfigInterface $backendConfig
  58. * @param \Magento\Framework\Stdlib\DateTime $dateTime
  59. * @param Encryptor $encryptor
  60. */
  61. public function __construct(
  62. CustomerRepositoryInterface $customerRepository,
  63. CustomerRegistry $customerRegistry,
  64. ConfigInterface $backendConfig,
  65. \Magento\Framework\Stdlib\DateTime $dateTime,
  66. Encryptor $encryptor
  67. ) {
  68. $this->customerRepository = $customerRepository;
  69. $this->customerRegistry = $customerRegistry;
  70. $this->backendConfig = $backendConfig;
  71. $this->dateTime = $dateTime;
  72. $this->encryptor = $encryptor;
  73. }
  74. /**
  75. * @inheritdoc
  76. */
  77. public function processAuthenticationFailure($customerId)
  78. {
  79. $now = new \DateTime();
  80. $lockThreshold = $this->getLockThreshold();
  81. $maxFailures = $this->getMaxFailures();
  82. $customerSecure = $this->customerRegistry->retrieveSecureData($customerId);
  83. if (!($lockThreshold && $maxFailures)) {
  84. return;
  85. }
  86. $failuresNum = (int)$customerSecure->getFailuresNum() + 1;
  87. $firstFailureDate = $customerSecure->getFirstFailure();
  88. if ($firstFailureDate) {
  89. $firstFailureDate = new \DateTime($firstFailureDate);
  90. }
  91. $lockThreshInterval = new \DateInterval('PT' . $lockThreshold . 'S');
  92. $lockExpires = $customerSecure->getLockExpires();
  93. $lockExpired = ($lockExpires !== null) && ($now > new \DateTime($lockExpires));
  94. // set first failure date when this is the first failure or the lock is expired
  95. if (1 === $failuresNum || !$firstFailureDate || $lockExpired) {
  96. $customerSecure->setFirstFailure($this->dateTime->formatDate($now));
  97. $failuresNum = 1;
  98. $customerSecure->setLockExpires(null);
  99. // otherwise lock customer
  100. } elseif ($failuresNum >= $maxFailures) {
  101. $customerSecure->setLockExpires($this->dateTime->formatDate($now->add($lockThreshInterval)));
  102. }
  103. $customerSecure->setFailuresNum($failuresNum);
  104. $this->getCustomerAuthUpdate()->saveAuth($customerId);
  105. }
  106. /**
  107. * @inheritdoc
  108. */
  109. public function unlock($customerId)
  110. {
  111. $customerSecure = $this->customerRegistry->retrieveSecureData($customerId);
  112. $customerSecure->setFailuresNum(0);
  113. $customerSecure->setFirstFailure(null);
  114. $customerSecure->setLockExpires(null);
  115. $this->getCustomerAuthUpdate()->saveAuth($customerId);
  116. }
  117. /**
  118. * Get lock threshold
  119. *
  120. * @return int
  121. */
  122. protected function getLockThreshold()
  123. {
  124. return $this->backendConfig->getValue(self::LOCKOUT_THRESHOLD_PATH) * 60;
  125. }
  126. /**
  127. * Get max failures
  128. *
  129. * @return int
  130. */
  131. protected function getMaxFailures()
  132. {
  133. return $this->backendConfig->getValue(self::MAX_FAILURES_PATH);
  134. }
  135. /**
  136. * @inheritdoc
  137. */
  138. public function isLocked($customerId)
  139. {
  140. $currentCustomer = $this->customerRegistry->retrieve($customerId);
  141. return $currentCustomer->isCustomerLocked();
  142. }
  143. /**
  144. * @inheritdoc
  145. */
  146. public function authenticate($customerId, $password)
  147. {
  148. $customerSecure = $this->customerRegistry->retrieveSecureData($customerId);
  149. $hash = $customerSecure->getPasswordHash() ?? '';
  150. if (!$this->encryptor->validateHash($password, $hash)) {
  151. $this->processAuthenticationFailure($customerId);
  152. if ($this->isLocked($customerId)) {
  153. throw new UserLockedException(__('The account is locked.'));
  154. }
  155. throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
  156. }
  157. return true;
  158. }
  159. /**
  160. * Get customer authentication update model
  161. *
  162. * @return \Magento\Customer\Model\CustomerAuthUpdate
  163. * @deprecated 100.1.1
  164. */
  165. private function getCustomerAuthUpdate()
  166. {
  167. if ($this->customerAuthUpdate === null) {
  168. $this->customerAuthUpdate =
  169. \Magento\Framework\App\ObjectManager::getInstance()->get(CustomerAuthUpdate::class);
  170. }
  171. return $this->customerAuthUpdate;
  172. }
  173. }