Skip to content

Commit 150b821

Browse files
Merge MAGETWO-70943 into 2.3-bugfixes-031018
2 parents a30f2ae + c4c8af6 commit 150b821

File tree

12 files changed

+199
-2762
lines changed

12 files changed

+199
-2762
lines changed

app/code/Magento/Customer/Api/AccountManagementInterface.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace Magento\Customer\Api;
99

10+
use Magento\Framework\Exception\InputException;
11+
1012
/**
1113
* Interface for managing customers accounts.
1214
* @api
@@ -144,19 +146,24 @@ public function initiatePasswordReset($email, $template, $websiteId = null);
144146
/**
145147
* Reset customer password.
146148
*
147-
* @param string $email
149+
* @param string $email If empty value given then the customer
150+
* will be matched by the RP token.
148151
* @param string $resetToken
149152
* @param string $newPassword
153+
*
150154
* @return bool true on success
151155
* @throws \Magento\Framework\Exception\LocalizedException
156+
* @throws InputException
152157
*/
153158
public function resetPassword($email, $resetToken, $newPassword);
154159

155160
/**
156161
* Check if password reset token is valid.
157162
*
158-
* @param int $customerId
163+
* @param int $customerId If null is given then a customer
164+
* will be matched by the RP token.
159165
* @param string $resetPasswordLinkToken
166+
*
160167
* @return bool True if the token is valid
161168
* @throws \Magento\Framework\Exception\State\InputMismatchException If token is mismatched
162169
* @throws \Magento\Framework\Exception\State\ExpiredException If token is expired

app/code/Magento/Customer/Controller/Account/CreatePassword.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
<?php
22
/**
3-
*
43
* Copyright © Magento, Inc. All rights reserved.
54
* See COPYING.txt for license details.
65
*/
76
namespace Magento\Customer\Controller\Account;
87

98
use Magento\Customer\Api\AccountManagementInterface;
109
use Magento\Customer\Model\Session;
10+
use Magento\Framework\App\Action\HttpGetActionInterface;
1111
use Magento\Framework\View\Result\PageFactory;
1212
use Magento\Framework\App\Action\Context;
1313

14-
class CreatePassword extends \Magento\Customer\Controller\AbstractAccount
14+
/**
15+
* Class CreatePassword
16+
*
17+
* @package Magento\Customer\Controller\Account
18+
*/
19+
class CreatePassword extends \Magento\Customer\Controller\AbstractAccount implements HttpGetActionInterface
1520
{
1621
/**
1722
* @var \Magento\Customer\Api\AccountManagementInterface
@@ -54,27 +59,27 @@ public function __construct(
5459
public function execute()
5560
{
5661
$resetPasswordToken = (string)$this->getRequest()->getParam('token');
57-
$customerId = (int)$this->getRequest()->getParam('id');
58-
$isDirectLink = $resetPasswordToken != '' && $customerId != 0;
62+
$isDirectLink = $resetPasswordToken != '';
5963
if (!$isDirectLink) {
6064
$resetPasswordToken = (string)$this->session->getRpToken();
61-
$customerId = (int)$this->session->getRpCustomerId();
6265
}
6366

6467
try {
65-
$this->accountManagement->validateResetPasswordLinkToken($customerId, $resetPasswordToken);
68+
$this->accountManagement->validateResetPasswordLinkToken(null, $resetPasswordToken);
6669

6770
if ($isDirectLink) {
6871
$this->session->setRpToken($resetPasswordToken);
69-
$this->session->setRpCustomerId($customerId);
7072
$resultRedirect = $this->resultRedirectFactory->create();
7173
$resultRedirect->setPath('*/*/createpassword');
74+
7275
return $resultRedirect;
7376
} else {
7477
/** @var \Magento\Framework\View\Result\Page $resultPage */
7578
$resultPage = $this->resultPageFactory->create();
76-
$resultPage->getLayout()->getBlock('resetPassword')->setCustomerId($customerId)
79+
$resultPage->getLayout()
80+
->getBlock('resetPassword')
7781
->setResetPasswordLinkToken($resetPasswordToken);
82+
7883
return $resultPage;
7984
}
8085
} catch (\Exception $exception) {

app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?php
22
/**
3-
*
43
* Copyright © Magento, Inc. All rights reserved.
54
* See COPYING.txt for license details.
65
*/
@@ -10,11 +9,16 @@
109
use Magento\Customer\Api\CustomerRepositoryInterface;
1110
use Magento\Customer\Model\Session;
1211
use Magento\Framework\App\Action\Context;
12+
use Magento\Framework\App\Action\HttpPostActionInterface;
1313
use Magento\Framework\Exception\InputException;
1414
use Magento\Customer\Model\Customer\CredentialsValidator;
15-
use Magento\Framework\App\ObjectManager;
1615

17-
class ResetPasswordPost extends \Magento\Customer\Controller\AbstractAccount
16+
/**
17+
* Class ResetPasswordPost
18+
*
19+
* @package Magento\Customer\Controller\Account
20+
*/
21+
class ResetPasswordPost extends \Magento\Customer\Controller\AbstractAccount implements HttpPostActionInterface
1822
{
1923
/**
2024
* @var \Magento\Customer\Api\AccountManagementInterface
@@ -31,17 +35,14 @@ class ResetPasswordPost extends \Magento\Customer\Controller\AbstractAccount
3135
*/
3236
protected $session;
3337

34-
/**
35-
* @var CredentialsValidator
36-
*/
37-
private $credentialsValidator;
38-
3938
/**
4039
* @param Context $context
4140
* @param Session $customerSession
4241
* @param AccountManagementInterface $accountManagement
4342
* @param CustomerRepositoryInterface $customerRepository
4443
* @param CredentialsValidator|null $credentialsValidator
44+
*
45+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
4546
*/
4647
public function __construct(
4748
Context $context,
@@ -53,8 +54,6 @@ public function __construct(
5354
$this->session = $customerSession;
5455
$this->accountManagement = $accountManagement;
5556
$this->customerRepository = $customerRepository;
56-
$this->credentialsValidator = $credentialsValidator ?: ObjectManager::getInstance()
57-
->get(CredentialsValidator::class);
5857
parent::__construct($context);
5958
}
6059

@@ -70,29 +69,32 @@ public function execute()
7069
/** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */
7170
$resultRedirect = $this->resultRedirectFactory->create();
7271
$resetPasswordToken = (string)$this->getRequest()->getQuery('token');
73-
$customerId = (int)$this->getRequest()->getQuery('id');
7472
$password = (string)$this->getRequest()->getPost('password');
7573
$passwordConfirmation = (string)$this->getRequest()->getPost('password_confirmation');
7674

7775
if ($password !== $passwordConfirmation) {
7876
$this->messageManager->addError(__("New Password and Confirm New Password values didn't match."));
79-
$resultRedirect->setPath('*/*/createPassword', ['id' => $customerId, 'token' => $resetPasswordToken]);
77+
$resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]);
78+
8079
return $resultRedirect;
8180
}
8281
if (iconv_strlen($password) <= 0) {
8382
$this->messageManager->addError(__('Please enter a new password.'));
84-
$resultRedirect->setPath('*/*/createPassword', ['id' => $customerId, 'token' => $resetPasswordToken]);
83+
$resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]);
84+
8585
return $resultRedirect;
8686
}
8787

8888
try {
89-
$customerEmail = $this->customerRepository->getById($customerId)->getEmail();
90-
$this->credentialsValidator->checkPasswordDifferentFromEmail($customerEmail, $password);
91-
$this->accountManagement->resetPassword($customerEmail, $resetPasswordToken, $password);
89+
$this->accountManagement->resetPassword(
90+
null,
91+
$resetPasswordToken,
92+
$password
93+
);
9294
$this->session->unsRpToken();
93-
$this->session->unsRpCustomerId();
9495
$this->messageManager->addSuccess(__('You updated your password.'));
9596
$resultRedirect->setPath('*/*/login');
97+
9698
return $resultRedirect;
9799
} catch (InputException $e) {
98100
$this->messageManager->addError($e->getMessage());
@@ -102,7 +104,8 @@ public function execute()
102104
} catch (\Exception $exception) {
103105
$this->messageManager->addError(__('Something went wrong while saving the new password.'));
104106
}
105-
$resultRedirect->setPath('*/*/createPassword', ['id' => $customerId, 'token' => $resetPasswordToken]);
107+
$resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]);
108+
106109
return $resultRedirect;
107110
}
108111
}

app/code/Magento/Customer/Model/AccountManagement.php

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Magento\Customer\Model\Metadata\Validator;
2121
use Magento\Eav\Model\Validator\Attribute\Backend;
2222
use Magento\Framework\Api\ExtensibleDataObjectConverter;
23+
use Magento\Framework\Api\SearchCriteriaBuilder;
2324
use Magento\Framework\App\Area;
2425
use Magento\Framework\App\Config\ScopeConfigInterface;
2526
use Magento\Framework\App\ObjectManager;
@@ -41,6 +42,7 @@
4142
use Magento\Framework\Intl\DateTimeFactory;
4243
use Magento\Framework\Mail\Template\TransportBuilder;
4344
use Magento\Framework\Math\Random;
45+
use Magento\Framework\Phrase;
4446
use Magento\Framework\Reflection\DataObjectProcessor;
4547
use Magento\Framework\Registry;
4648
use Magento\Framework\Stdlib\DateTime;
@@ -326,6 +328,11 @@ class AccountManagement implements AccountManagementInterface
326328
*/
327329
private $accountConfirmation;
328330

331+
/**
332+
* @var SearchCriteriaBuilder
333+
*/
334+
private $searchCriteriaBuilder;
335+
329336
/**
330337
* @param CustomerFactory $customerFactory
331338
* @param ManagerInterface $eventManager
@@ -356,6 +363,7 @@ class AccountManagement implements AccountManagementInterface
356363
* @param SessionManagerInterface|null $sessionManager
357364
* @param SaveHandlerInterface|null $saveHandler
358365
* @param CollectionFactory|null $visitorCollectionFactory
366+
* @param SearchCriteriaBuilder|null $searchCriteriaBuilder
359367
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
360368
*/
361369
public function __construct(
@@ -387,7 +395,8 @@ public function __construct(
387395
AccountConfirmation $accountConfirmation = null,
388396
SessionManagerInterface $sessionManager = null,
389397
SaveHandlerInterface $saveHandler = null,
390-
CollectionFactory $visitorCollectionFactory = null
398+
CollectionFactory $visitorCollectionFactory = null,
399+
SearchCriteriaBuilder $searchCriteriaBuilder = null
391400
) {
392401
$this->customerFactory = $customerFactory;
393402
$this->eventManager = $eventManager;
@@ -423,6 +432,8 @@ public function __construct(
423432
?: ObjectManager::getInstance()->get(SaveHandlerInterface::class);
424433
$this->visitorCollectionFactory = $visitorCollectionFactory
425434
?: ObjectManager::getInstance()->get(CollectionFactory::class);
435+
$this->searchCriteriaBuilder = $searchCriteriaBuilder
436+
?: ObjectManager::getInstance()->get(SearchCriteriaBuilder::class);
426437
}
427438

428439
/**
@@ -591,6 +602,43 @@ public function initiatePasswordReset($email, $template, $websiteId = null)
591602
return false;
592603
}
593604

605+
/**
606+
* Match a customer by their RP token.
607+
*
608+
* @param string $rpToken
609+
* @throws ExpiredException
610+
* @throws NoSuchEntityException
611+
*
612+
* @return CustomerInterface
613+
* @throws LocalizedException
614+
*/
615+
private function matchCustomerByRpToken(string $rpToken): CustomerInterface
616+
{
617+
$this->searchCriteriaBuilder->addFilter(
618+
'rp_token',
619+
$rpToken
620+
);
621+
$this->searchCriteriaBuilder->setPageSize(1);
622+
$found = $this->customerRepository->getList(
623+
$this->searchCriteriaBuilder->create()
624+
);
625+
if ($found->getTotalCount() > 1) {
626+
//Failed to generated unique RP token
627+
throw new ExpiredException(
628+
new Phrase('Reset password token expired.')
629+
);
630+
}
631+
if ($found->getTotalCount() === 0) {
632+
//Customer with such token not found.
633+
throw NoSuchEntityException::singleField(
634+
'rp_token',
635+
$rpToken
636+
);
637+
}
638+
//Unique customer found.
639+
return $found->getItems()[0];
640+
}
641+
594642
/**
595643
* Handle not supported template
596644
*
@@ -615,16 +663,24 @@ private function handleUnknownTemplate($template)
615663
*/
616664
public function resetPassword($email, $resetToken, $newPassword)
617665
{
618-
$customer = $this->customerRepository->get($email);
666+
if (!$email) {
667+
$customer = $this->matchCustomerByRpToken($resetToken);
668+
$email = $customer->getEmail();
669+
} else {
670+
$customer = $this->customerRepository->get($email);
671+
}
619672
//Validate Token and new password strength
620673
$this->validateResetPasswordToken($customer->getId(), $resetToken);
674+
$this->credentialsValidator->checkPasswordDifferentFromEmail(
675+
$email,
676+
$newPassword
677+
);
621678
$this->checkPasswordStrength($newPassword);
622679
//Update secure data
623680
$customerSecure = $this->customerRegistry->retrieveSecureData($customer->getId());
624681
$customerSecure->setRpToken(null);
625682
$customerSecure->setRpTokenCreatedAt(null);
626683
$customerSecure->setPasswordHash($this->createPasswordHash($newPassword));
627-
$this->getAuthentication()->unlock($customer->getId());
628684
$this->sessionManager->destroy();
629685
$this->destroyCustomerSessions($customer->getId());
630686
$this->customerRepository->save($customer);
@@ -955,6 +1011,8 @@ protected function createPasswordHash($password)
9551011
}
9561012

9571013
/**
1014+
* Returns eval validator
1015+
*
9581016
* @return Backend
9591017
*/
9601018
private function getEavValidator()
@@ -1033,32 +1091,36 @@ public function isCustomerInStore($customerWebsiteId, $storeId)
10331091
* @throws \Magento\Framework\Exception\State\ExpiredException If token is expired
10341092
* @throws \Magento\Framework\Exception\InputException If token or customer id is invalid
10351093
* @throws \Magento\Framework\Exception\NoSuchEntityException If customer doesn't exist
1094+
* @throws LocalizedException
10361095
*/
10371096
private function validateResetPasswordToken($customerId, $resetPasswordLinkToken)
10381097
{
1039-
if (empty($customerId) || $customerId < 0) {
1098+
if ($customerId !== null && $customerId <= 0) {
10401099
throw new InputException(
10411100
__(
10421101
'Invalid value of "%value" provided for the %fieldName field.',
10431102
['value' => $customerId, 'fieldName' => 'customerId']
10441103
)
10451104
);
10461105
}
1106+
1107+
if ($customerId === null) {
1108+
//Looking for the customer.
1109+
$customerId = $this->matchCustomerByRpToken($resetPasswordLinkToken)
1110+
->getId();
1111+
}
10471112
if (!is_string($resetPasswordLinkToken) || empty($resetPasswordLinkToken)) {
10481113
$params = ['fieldName' => 'resetPasswordLinkToken'];
10491114
throw new InputException(__('"%fieldName" is required. Enter and try again.', $params));
10501115
}
1051-
10521116
$customerSecureData = $this->customerRegistry->retrieveSecureData($customerId);
10531117
$rpToken = $customerSecureData->getRpToken();
10541118
$rpTokenCreatedAt = $customerSecureData->getRpTokenCreatedAt();
1055-
10561119
if (!Security::compareStrings($rpToken, $resetPasswordLinkToken)) {
10571120
throw new InputMismatchException(__('The password token is mismatched. Reset and try again.'));
10581121
} elseif ($this->isResetPasswordLinkTokenExpired($rpToken, $rpTokenCreatedAt)) {
10591122
throw new ExpiredException(__('The password token is expired. Reset and try again.'));
10601123
}
1061-
10621124
return true;
10631125
}
10641126

@@ -1141,6 +1203,7 @@ protected function sendPasswordResetNotificationEmail($customer)
11411203
* @param int|string|null $defaultStoreId
11421204
* @return int
11431205
* @deprecated 100.1.0
1206+
* @throws LocalizedException
11441207
*/
11451208
protected function getWebsiteStoreId($customer, $defaultStoreId = null)
11461209
{
@@ -1153,6 +1216,8 @@ protected function getWebsiteStoreId($customer, $defaultStoreId = null)
11531216
}
11541217

11551218
/**
1219+
* Return array with template types
1220+
*
11561221
* @return array
11571222
* @deprecated 100.1.0
11581223
*/

0 commit comments

Comments
 (0)