diff --git a/app/code/Magento/Security/Model/Config.php b/app/code/Magento/Security/Model/Config.php
index d31495c7fad6b..cb21a0b134af2 100644
--- a/app/code/Magento/Security/Model/Config.php
+++ b/app/code/Magento/Security/Model/Config.php
@@ -24,10 +24,17 @@ class Config implements ConfigInterface
*/
const XML_PATH_ADMIN_AREA = 'admin/security/';
+ /**
+ * Configuration path to frontend area
+ */
+ const XML_PATH_FRONTEND_AREA = 'customer/password/';
+
/**
* Configuration path to fronted area
+ * @deprecated
+ * @see \Magento\Security\Model\Config::XML_PATH_FRONTEND_AREA
*/
- const XML_PATH_FRONTED_AREA = 'customer/password/';
+ const XML_PATH_FRONTED_AREA = self::XML_PATH_FRONTEND_AREA;
/**
* Configuration path to admin account sharing
@@ -134,7 +141,7 @@ protected function getXmlPathPrefix()
if ($this->scope->getCurrentScope() == \Magento\Framework\App\Area::AREA_ADMINHTML) {
return self::XML_PATH_ADMIN_AREA;
}
- return self::XML_PATH_FRONTED_AREA;
+ return self::XML_PATH_FRONTEND_AREA;
}
/**
diff --git a/app/code/Magento/Security/Model/Plugin/AccountManagement.php b/app/code/Magento/Security/Model/Plugin/AccountManagement.php
index 14c20f9335646..a7bd79b90eae9 100644
--- a/app/code/Magento/Security/Model/Plugin/AccountManagement.php
+++ b/app/code/Magento/Security/Model/Plugin/AccountManagement.php
@@ -5,10 +5,12 @@
*/
namespace Magento\Security\Model\Plugin;
-use Magento\Security\Model\SecurityManager;
use Magento\Customer\Model\AccountManagement as AccountManagementOriginal;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Config\ScopeInterface;
use Magento\Framework\Exception\SecurityViolationException;
use Magento\Security\Model\PasswordResetRequestEvent;
+use Magento\Security\Model\SecurityManager;
/**
* Magento\Customer\Model\AccountManagement decorator
@@ -30,21 +32,29 @@ class AccountManagement
*/
protected $passwordRequestEvent;
+ /**
+ * @var ScopeInterface
+ */
+ private $scope;
+
/**
* AccountManagement constructor.
*
* @param \Magento\Framework\App\RequestInterface $request
* @param SecurityManager $securityManager
* @param int $passwordRequestEvent
+ * @param ScopeInterface $scope
*/
public function __construct(
\Magento\Framework\App\RequestInterface $request,
\Magento\Security\Model\SecurityManager $securityManager,
- $passwordRequestEvent = PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
+ $passwordRequestEvent = PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST,
+ ScopeInterface $scope = null
) {
$this->request = $request;
$this->securityManager = $securityManager;
$this->passwordRequestEvent = $passwordRequestEvent;
+ $this->scope = $scope ?: ObjectManager::getInstance()->get(ScopeInterface::class);
}
/**
@@ -63,10 +73,14 @@ public function beforeInitiatePasswordReset(
$template,
$websiteId = null
) {
- $this->securityManager->performSecurityCheck(
- $this->passwordRequestEvent,
- $email
- );
+ if ($this->scope->getCurrentScope() == \Magento\Framework\App\Area::AREA_FRONTEND
+ || $this->passwordRequestEvent == PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST) {
+ $this->securityManager->performSecurityCheck(
+ $this->passwordRequestEvent,
+ $email
+ );
+ }
+
return [$email, $template, $websiteId];
}
}
diff --git a/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php
index 27d594e001efb..29b3fb814d07e 100644
--- a/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php
+++ b/app/code/Magento/Security/Test/Unit/Model/ConfigTest.php
@@ -176,7 +176,7 @@ protected function getXmlPathPrefix($scope)
if ($scope == \Magento\Framework\App\Area::AREA_ADMINHTML) {
return \Magento\Security\Model\Config::XML_PATH_ADMIN_AREA;
}
- return \Magento\Security\Model\Config::XML_PATH_FRONTED_AREA;
+ return \Magento\Security\Model\Config::XML_PATH_FRONTEND_AREA;
}
/**
diff --git a/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php b/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php
index 74ae9f87a1850..57d70ad5f24be 100644
--- a/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php
+++ b/app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php
@@ -3,10 +3,13 @@
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-
namespace Magento\Security\Test\Unit\Model\Plugin;
+use Magento\Customer\Model\AccountManagement;
+use Magento\Framework\App\Area;
+use Magento\Framework\Config\ScopeInterface;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Security\Model\PasswordResetRequestEvent;
/**
* Test class for \Magento\Security\Model\Plugin\AccountManagement testing
@@ -19,20 +22,25 @@ class AccountManagementTest extends \PHPUnit_Framework_TestCase
protected $model;
/**
- * @var \Magento\Framework\App\RequestInterface
+ * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $request;
/**
- * @var \Magento\Security\Model\SecurityManager
+ * @var \Magento\Security\Model\SecurityManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $securityManager;
/**
- * @var \Magento\Customer\Model\AccountManagement
+ * @var AccountManagement|\PHPUnit_Framework_MockObject_MockObject
*/
protected $accountManagement;
+ /**
+ * @var ScopeInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $scope;
+
/**
* @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
*/
@@ -46,50 +54,49 @@ public function setUp()
{
$this->objectManager = new ObjectManager($this);
- $this->request = $this->getMock(
- '\Magento\Framework\App\RequestInterface',
- [],
- [],
- '',
- false
- );
+ $this->request = $this->getMock(\Magento\Framework\App\RequestInterface::class);
- $this->securityManager = $this->getMock(
- '\Magento\Security\Model\SecurityManager',
- ['performSecurityCheck'],
- [],
- '',
- false
- );
+ $this->securityManager = $this->getMockBuilder(
+ \Magento\Security\Model\SecurityManager::class
+ )->setMethods(
+ ['performSecurityCheck']
+ )->disableOriginalConstructor()->getMock();
- $this->accountManagement = $this->getMock(
- '\Magento\Customer\Model\AccountManagement',
- [],
- [],
- '',
- false
- );
+ $this->accountManagement = $this->getMockBuilder(
+ AccountManagement::class
+ )->disableOriginalConstructor()->getMock();
- $this->model = $this->objectManager->getObject(
- '\Magento\Security\Model\Plugin\AccountManagement',
- [
- 'request' => $this->request,
- 'securityManager' => $this->securityManager
- ]
- );
+ $this->scope = $this->getMock(ScopeInterface::class);
}
/**
- * @return void
+ * @param $area
+ * @param $passwordRequestEvent
+ * @param $expectedTimes
+ * @dataProvider beforeInitiatePasswordResetDataProvider
*/
- public function testBeforeInitiatePasswordReset()
+ public function testBeforeInitiatePasswordReset($area, $passwordRequestEvent, $expectedTimes)
{
$email = 'test@example.com';
- $template = \Magento\Customer\Model\AccountManagement::EMAIL_RESET;
+ $template = AccountManagement::EMAIL_RESET;
- $this->securityManager->expects($this->once())
+ $this->model = $this->objectManager->getObject(
+ \Magento\Security\Model\Plugin\AccountManagement::class,
+ [
+ 'passwordRequestEvent' => $passwordRequestEvent,
+ 'request' => $this->request,
+ 'securityManager' => $this->securityManager,
+ 'scope' => $this->scope
+ ]
+ );
+
+ $this->scope->expects($this->once())
+ ->method('getCurrentScope')
+ ->willReturn($area);
+
+ $this->securityManager->expects($this->exactly($expectedTimes))
->method('performSecurityCheck')
- ->with(\Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, $email)
+ ->with($passwordRequestEvent, $email)
->willReturnSelf();
$this->model->beforeInitiatePasswordReset(
@@ -98,4 +105,18 @@ public function testBeforeInitiatePasswordReset()
$template
);
}
+
+ /**
+ * @return array
+ */
+ public function beforeInitiatePasswordResetDataProvider()
+ {
+ return [
+ [Area::AREA_ADMINHTML, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 0],
+ [Area::AREA_ADMINHTML, PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, 1],
+ [Area::AREA_FRONTEND, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 1],
+ // This should never happen, but let's cover it with tests
+ [Area::AREA_FRONTEND, PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, 1],
+ ];
+ }
}
diff --git a/app/code/Magento/Security/etc/adminhtml/di.xml b/app/code/Magento/Security/etc/adminhtml/di.xml
index 623dab691e0ba..d20f31376e7a5 100644
--- a/app/code/Magento/Security/etc/adminhtml/di.xml
+++ b/app/code/Magento/Security/etc/adminhtml/di.xml
@@ -17,7 +17,7 @@
- Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST
+ Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php
index 876e0d1e5dd26..d66c214250a86 100644
--- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php
@@ -8,16 +8,12 @@
use Magento\Customer\Api\Data\CustomerInterface as Customer;
use Magento\Customer\Model\AccountManagement;
use Magento\Framework\Exception\InputException;
-use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
+use Magento\Newsletter\Model\Subscriber;
+use Magento\Security\Model\Config;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Helper\Customer as CustomerHelper;
use Magento\TestFramework\TestCase\WebapiAbstract;
-use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
-use Magento\Security\Model\Config;
-use Magento\Newsletter\Model\Plugin\CustomerPlugin;
-use Magento\Framework\Webapi\Rest\Request as RestRequest;
-use Magento\Newsletter\Model\Subscriber;
-use Magento\Customer\Model\Data\Customer as CustomerData;
/**
* Test class for Magento\Customer\Api\AccountManagementInterface
@@ -112,16 +108,16 @@ public function setUp()
$this->initSubscriber();
if ($this->config->getConfigDataValue(
- Config::XML_PATH_FRONTED_AREA .
+ Config::XML_PATH_FRONTEND_AREA .
Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE
) != 0) {
$this->configValue = $this->config
->getConfigDataValue(
- Config::XML_PATH_FRONTED_AREA .
+ Config::XML_PATH_FRONTEND_AREA .
Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE
);
$this->config->setDataByPath(
- Config::XML_PATH_FRONTED_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
+ Config::XML_PATH_FRONTEND_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
0
);
$this->config->save();
@@ -150,7 +146,7 @@ public function tearDown()
}
}
$this->config->setDataByPath(
- Config::XML_PATH_FRONTED_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
+ Config::XML_PATH_FRONTEND_AREA . Config::XML_PATH_PASSWORD_RESET_PROTECTION_TYPE,
$this->configValue
);
$this->config->save();
@@ -158,7 +154,8 @@ public function tearDown()
unset($this->subscriber);
}
- private function initSubscriber() {
+ private function initSubscriber()
+ {
$this->subscriber = Bootstrap::getObjectManager()->create(
'Magento\Newsletter\Model\Subscriber'
);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
index 7a19ade8d8566..f76055dbc3fc6 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php
@@ -20,10 +20,11 @@ class ResetPasswordTest extends \Magento\TestFramework\TestCase\AbstractBackendC
protected $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/';
/**
- * Checks reset password functionality with default settings and customer reset request event.
+ * Checks reset password functionality with no restrictive settings and customer reset request event.
+ * Admin is not affected by this security check, so reset password email must be sent.
*
- * @magentoConfigFixture current_store admin/security/limit_password_reset_requests_method 1
- * @magentoConfigFixture current_store admin/security/min_time_between_password_reset_requests 10
+ * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0
+ * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 0
* @magentoDataFixture Magento/Customer/_files/customer.php
*/
public function testResetPasswordSuccess()
@@ -40,11 +41,57 @@ public function testResetPasswordSuccess()
$this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
}
+ /**
+ * Checks reset password functionality with default restrictive min time between
+ * password reset requests and customer reset request event.
+ * Admin is not affected by this security check, so reset password email must be sent.
+ *
+ * @magentoConfigFixture current_store customer/password/max_number_password_reset_requests 0
+ * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 10
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testResetPasswordMinTimeError()
+ {
+ $this->passwordResetRequestEventCreate(
+ \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
+ );
+ $this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->dispatch('backend/customer/index/resetPassword');
+ $this->assertSessionMessages(
+ $this->equalTo(['The customer will receive an email with a link to reset password.']),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
+ }
+
+ /**
+ * Checks reset password functionality with default restrictive limited number
+ * password reset requests and customer reset request event.
+ * Admin is not affected by this security check, so reset password email must be sent.
+ *
+ * @magentoConfigFixture current_store customer/password/max_number_password_reset_requests 1
+ * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 0
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testResetPasswordLimitError()
+ {
+ $this->passwordResetRequestEventCreate(
+ \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST
+ );
+ $this->getRequest()->setPostValue(['customer_id' => '1']);
+ $this->dispatch('backend/customer/index/resetPassword');
+ $this->assertSessionMessages(
+ $this->equalTo(['The customer will receive an email with a link to reset password.']),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
+ }
+
/**
* Checks reset password functionality with default settings, customer and admin reset request events.
*
- * @magentoConfigFixture current_store admin/security/limit_password_reset_requests_method 1
- * @magentoConfigFixture current_store admin/security/min_time_between_password_reset_requests 10
+ * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 1
+ * @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 10
* @magentoConfigFixture current_store contact/email/recipient_email hello@example.com
* @magentoDataFixture Magento/Customer/_files/customer.php
*/
@@ -59,10 +106,8 @@ public function testResetPasswordWithSecurityViolationException()
$this->getRequest()->setPostValue(['customer_id' => '1']);
$this->dispatch('backend/customer/index/resetPassword');
$this->assertSessionMessages(
- $this->equalTo(
- ['Too many password reset requests. Please wait and try again or contact hello@example.com.']
- ),
- \Magento\Framework\Message\MessageInterface::TYPE_ERROR
+ $this->equalTo(['The customer will receive an email with a link to reset password.']),
+ \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
);
$this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit'));
}