Skip to content

Commit be42227

Browse files
committed
Merge remote-tracking branch 'origin/ACP2E-3992' into PR_2025_08_04_chittima
2 parents b0abb22 + 14aab2a commit be42227

File tree

5 files changed

+52
-9
lines changed

5 files changed

+52
-9
lines changed

app/code/Magento/Security/Model/Plugin/AccountManagement.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2016 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -80,7 +80,8 @@ public function beforeInitiatePasswordReset(
8080
) {
8181
if ($this->scope->getCurrentScope() == Area::AREA_FRONTEND
8282
|| $this->passwordRequestEvent == PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST
83-
|| ($this->scope->getCurrentScope() == Area::AREA_WEBAPI_REST
83+
|| (($this->scope->getCurrentScope() == Area::AREA_WEBAPI_REST
84+
|| $this->scope->getCurrentScope() == Area::AREA_GRAPHQL)
8485
&& $this->passwordRequestEvent == PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST)) {
8586
$this->securityManager->performSecurityCheck(
8687
$this->passwordRequestEvent,

app/code/Magento/Security/Test/Unit/Model/Plugin/AccountManagementTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2016 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -117,6 +117,7 @@ public static function beforeInitiatePasswordResetDataProvider()
117117
[Area::AREA_ADMINHTML, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 0],
118118
[Area::AREA_ADMINHTML, PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, 1],
119119
[Area::AREA_FRONTEND, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 1],
120+
[Area::AREA_GRAPHQL, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 1],
120121
// This should never happen, but let's cover it with tests
121122
[Area::AREA_FRONTEND, PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST, 1],
122123
[Area::AREA_WEBAPI_REST, PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, 1],

dev/tests/integration/framework/Magento/TestFramework/ApplicationStateComparator/_files/state-skip-list.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@
290290
Magento\Framework\Cache\LockGuardedCacheLoader::class => null,
291291
Magento\Framework\View\Asset\PreProcessor\Pool::class => null,
292292
Magento\Framework\App\Area::class => null,
293+
Magento\Security\Model\ResourceModel\PasswordResetRequestEvent::class => null,
293294
Magento\Store\Model\Store\Interceptor::class => null,
294295
Magento\Framework\TestFramework\ApplicationStateComparator\Comparator::class => null, // Yes, our test uses mutable state itself :-)
295296
Magento\Framework\GraphQl\Query\QueryParser::class => null, // reloads as a ReloadProcessor

dev/tests/integration/testsuite/Magento/GraphQl/App/GraphQlCustomerMutationsTest.php

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

88
namespace Magento\GraphQl\App;
99

10+
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\Customer\Api\AccountManagementInterface;
12+
use Magento\Framework\App\Area;
13+
use Magento\Framework\App\State;
14+
use Magento\Framework\Exception\SecurityViolationException;
15+
use Magento\Security\Model\ResourceModel\PasswordResetRequestEvent\Collection as PasswordResetRequestEventCollection;
1016
use Magento\Customer\Api\CustomerRepositoryInterface;
1117
use Magento\Framework\Exception\NoSuchEntityException;
1218
use Magento\Framework\Registry;
@@ -45,6 +51,9 @@ protected function setUp(): void
4551
*/
4652
protected function tearDown(): void
4753
{
54+
$this->graphQlStateDiff->getTestObjectManager()
55+
->create(PasswordResetRequestEventCollection::class)
56+
->deleteRecordsOlderThen(time() + 1);
4857
$this->graphQlStateDiff->tearDown();
4958
$this->graphQlStateDiff = null;
5059
parent::tearDown();
@@ -155,6 +164,37 @@ public function testResetPassword(): void
155164
);
156165
}
157166

167+
/**
168+
* Test that GraphQL password reset requests are subject to security checks (rate limiting)
169+
* This test verifies our fix to include GraphQL area in security checks
170+
*
171+
* @magentoDataFixture Magento/Customer/_files/customer.php
172+
* @magentoConfigFixture current_store customer/password/password_reset_protection_type 1
173+
* @magentoConfigFixture current_store customer/password/max_number_password_reset_requests 1
174+
* @magentoConfigFixture current_store customer/password/min_time_between_password_reset_requests 10
175+
* @return void
176+
*/
177+
public function testGraphQlPasswordResetSecurityLimiting(): void
178+
{
179+
$email = '[email protected]';
180+
$query = $this->getRequestPasswordResetEmailMutation();
181+
$this->graphQlStateDiff->testState(
182+
$query,
183+
['email' => $email],
184+
[],
185+
[],
186+
'requestPasswordResetEmail',
187+
'"data":{"requestPasswordResetEmail":',
188+
$this
189+
);
190+
$this->expectException(SecurityViolationException::class);
191+
$objectManager = Bootstrap::getObjectManager();
192+
$accountManagement = $objectManager->get(AccountManagementInterface::class);
193+
$appState = $objectManager->get(State::class);
194+
$appState->setAreaCode(Area::AREA_GRAPHQL);
195+
$accountManagement->initiatePasswordReset($email, 'reset_password_template');
196+
}
197+
158198
/**
159199
* @magentoDataFixture Magento/Customer/_files/customer.php
160200
* @return void

dev/tests/integration/testsuite/Magento/GraphQl/App/State/GraphQlStateDiff.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* Copyright 2025 Adobe
3+
* Copyright 2023 Adobe
44
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
@@ -153,7 +153,7 @@ public function testState(
153153
} elseif ($operationName==='applyCouponToCart') {
154154
$this->removeCouponFromCart($variables);
155155
} elseif ($operationName==='resetPassword') {
156-
$variables2['resetPasswordToken'] = $this->getResetPasswordToken($variables['email']);
156+
$variables2['resetPasswordToken'] = $variables['resetPasswordToken'];
157157
$variables2['email'] = $variables['email'];
158158
$variables2['newPassword'] = $variables['newPassword'];
159159
}

0 commit comments

Comments
 (0)