Skip to content

Commit 740de39

Browse files
Merge pull request #91 from magento-cia/2.4.6-develop-2.4-develop-sync-101022
Sync of 2.4.6-develop with 2.4-develop
2 parents 356d37a + 0e72cef commit 740de39

File tree

8 files changed

+199
-39
lines changed

8 files changed

+199
-39
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="RecaptchaDisabledActionGroup">
12+
<annotations>
13+
<description>Disable admin login recaptcha</description>
14+
</annotations>
15+
16+
<amOnPage url="{{AdminStoreConfigurationPage.url}}" stepKey="navigateToAdminStoresConfiguration"/>
17+
<conditionalClick selector="{{RecaptchaFormSection.adminblock}}" dependentSelector="{{RecaptchaFormSection.label}}" visible="false" stepKey="expand_panel"/>
18+
<scrollTo selector="{{RecaptchaFormSection.label}}" stepKey="scroll_to_dropdown" />
19+
<wait time="2" stepKey="wait_for_Load"/>
20+
<checkOption selector="{{RecaptchaFormSection.useConfigSettings}}" stepKey="check_config_setting"/>
21+
<wait time="2" stepKey="wait_for_load1"/>
22+
<click selector="{{RecaptchaFormSection.saveConfig}}" stepKey="click_save"/>
23+
24+
</actionGroup>
25+
</actionGroups>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="RecaptchaEnabledActionGroup">
12+
<annotations>
13+
<description>Enable admin login recaptcha</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="optionValue" type="string" defaultValue="{{recaptchaForm.value}}"/>
17+
</arguments>
18+
19+
<amOnPage url="{{AdminStoreConfigurationPage.url}}" stepKey="navigateToAdminStoreConfiguration"/>
20+
<conditionalClick selector="{{RecaptchaFormSection.adminblock}}" dependentSelector="{{RecaptchaFormSection.label}}" visible="false" stepKey="expand_panel"/>
21+
<scrollTo selector="{{RecaptchaFormSection.label}}" stepKey="scrollToDropdownSelection" />
22+
<wait time="2" stepKey="waitForLoad"/>
23+
<uncheckOption selector="{{RecaptchaFormSection.useConfigSettings}}" stepKey="uncheckConfigSetting"/>
24+
<wait time="2" stepKey="waitForLoad_1"/>
25+
<selectOption selector="{{RecaptchaFormSection.optionvalue}}" userInput="{{optionValue}}" stepKey="setAttributeValue"/>
26+
<click selector="{{RecaptchaFormSection.saveConfig}}" stepKey="clickSave"/>
27+
28+
</actionGroup>
29+
</actionGroups>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
11+
<entity name="recaptchaForm">
12+
<data key="value">recaptcha_v3</data>
13+
</entity>
14+
<entity name="recaptchaFormDisabled">
15+
<data key="value"></data>
16+
</entity>
17+
</entities>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd">
11+
<page name="AdminStoreConfigurationPage" url="admin/system_config/edit/section/recaptcha_backend" area="admin" module="Magento_Backend">
12+
<section name="RecaptchaFormSection"/>
13+
</page>
14+
</pages>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
11+
<section name="RecaptchaFormSection">
12+
<element name="optionvalue" type="select" selector="#recaptcha_backend_type_for_user_login"/>
13+
<element name="saveConfig" type="button" selector="#save" timeout="30"/>
14+
<element name="label" type="input" selector="#row_recaptcha_backend_type_for_user_login"/>
15+
<element name="useConfigSettings" type="checkbox" selector="//input[@name='groups[type_for][fields][user_login][inherit]']"/>
16+
<element name="adminblock" type="button" selector='a#recaptcha_backend_type_for-head'/>
17+
</section>
18+
</sections>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="AdminLoginReCaptchaFunctionalityTest">
11+
<annotations>
12+
<features value="Backend"/>
13+
<stories value="Check for Recaptcha on the Admin Login page"/>
14+
<title value="Admin should be able to login with enabled recaptcha option"/>
15+
<description value="Admin should be able to login with enabled recaptcha option"/>
16+
<severity value="AVERAGE"/>
17+
<group value="login"/>
18+
<testCaseId value="AC-3179"/>
19+
</annotations>
20+
<before>
21+
<actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
22+
<actionGroup ref="RecaptchaEnabledActionGroup" stepKey="recaptchaEnabled"/>
23+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
24+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAgain11"/>
25+
<actionGroup ref="RecaptchaDisabledActionGroup" stepKey="recaptchaDisabled"/>
26+
</before>
27+
<after>
28+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
29+
</after>
30+
</test>
31+
</tests>

ReCaptchaUser/view/adminhtml/templates/recaptcha.phtml

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ $isInvisible = !empty($config['invisible']);
1313
class="admin-recaptcha-content<?=
1414
/* @noEscape */ !empty($renderingOptions['size']) ? ' size-' . $renderingOptions['size'] : '' ?>"></div>
1515
</div>
16-
1716
<script>
1817
require([
1918
'jquery'
@@ -27,36 +26,57 @@ $isInvisible = !empty($config['invisible']);
2726
element.src = 'https://www.google.com/recaptcha/api.js'
2827
+ '?onload=globalOnRecaptchaOnLoadCallback&render=explicit';
2928

30-
window.globalOnRecaptchaOnLoadCallback = function () {
31-
let token = '';
29+
let isRecaptchaLoaded = false;
30+
let token = '';
31+
let maxRetryAttempts = 5;
32+
let attempts = 0;
33+
let widgetId = 0;
34+
<?php if ($isInvisible): ?>
35+
$('#login-form').submit(function (event) {
36+
if (!token) {
37+
event.preventDefault(event);
38+
event.stopImmediatePropagation();
39+
event.stopPropagation();
3240

33-
this.widgetId = grecaptcha.render('admin-recaptcha', {
34-
<?php foreach ($renderingOptions as $key => $value): ?>
35-
'<?= $block->escapeJs($key) ?>': '<?= $block->escapeJs($value) ?>',
36-
<?php endforeach; ?>
37-
'callback': function (token) { // jscs:ignore jsDoc
38-
<?php if ($isInvisible): ?>
39-
this.token = token;
40-
$('#login-form').submit();
41-
<?php endif; ?>
42-
}.bind(this)
43-
});
41+
let attemptRecaptcha = () => {
42+
attempts++;
43+
if (attempts > maxRetryAttempts){
44+
console.error("Could not fetch invisible ReCaptcha token. Please refresh the page.");
45+
return;
46+
}
47+
if (!isRecaptchaLoaded) {
4448

45-
<?php if ($isInvisible): ?>
46-
$('#login-form').submit(function (event) {
47-
if (!this.token) {
48-
grecaptcha.execute(this.widgetId).then(
49-
function() {
49+
setTimeout(() => {
50+
attemptRecaptcha()
51+
}, 1000);
52+
return;
53+
}
54+
grecaptcha.execute(widgetId)
55+
.then( () => {
5056
event.preventDefault(event);
5157
event.stopImmediatePropagation();
52-
}, function(reason) {
53-
});
58+
event.stopPropagation();
59+
}, (reason) => { })
60+
.catch(err => { console.error(err); });
5461
}
55-
}.bind(this));
56-
<?php endif; ?>
57-
58-
}.bind(this);
62+
attemptRecaptcha();
63+
}
64+
});
65+
<?php endif; ?>
66+
window.globalOnRecaptchaOnLoadCallback = function () {
5967

68+
widgetId = grecaptcha.render('admin-recaptcha', {
69+
<?php foreach ($renderingOptions as $key => $value): ?>
70+
'<?= $block->escapeJs($key) ?>': '<?= $block->escapeJs($value) ?>',
71+
<?php endforeach; ?> 'callback': function (_token) {
72+
<?php if ($isInvisible): ?>
73+
token = _token;
74+
$('#login-form').unbind('submit');
75+
$('#login-form').submit();
76+
<?php endif; ?> }
77+
});
78+
isRecaptchaLoaded = true;
79+
}
6080
scriptTag.parentNode.insertBefore(element, scriptTag);
6181
});
6282
</script>

TwoFactorAuth/Test/Integration/UserConfigRequestManagerTest.php

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@
99
namespace Magento\TwoFactorAuth\Test\Integration;
1010

1111
use Magento\Framework\Acl\Builder;
12-
use Magento\TestFramework\Helper\Bootstrap;
12+
use Magento\Framework\Exception\AuthorizationException;
13+
use Magento\TestFramework\Bootstrap;
14+
use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper;
1315
use Magento\TestFramework\Mail\Template\TransportBuilderMock;
1416
use Magento\User\Model\User;
1517
use Magento\TwoFactorAuth\Api\TfaInterface;
1618
use Magento\TwoFactorAuth\Api\UserConfigRequestManagerInterface;
1719
use Magento\TwoFactorAuth\Api\UserConfigTokenManagerInterface;
1820
use Magento\TwoFactorAuth\Model\Provider\Engine\Google;
1921
use PHPUnit\Framework\TestCase;
22+
use Throwable;
2023

2124
/**
2225
* @magentoDbIsolation enabled
@@ -58,15 +61,15 @@ class UserConfigRequestManagerTest extends TestCase
5861
protected function setUp(): void
5962
{
6063
/** @var User $user */
61-
$user = Bootstrap::getObjectManager()->create(User::class);
62-
$user->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
64+
$user = BootstrapHelper::getObjectManager()->create(User::class);
65+
$user->loadByUsername(Bootstrap::ADMIN_NAME);
6366
$this->user = $user;
64-
$this->tfa = Bootstrap::getObjectManager()->get(TfaInterface::class);
65-
$this->transportBuilderMock = Bootstrap::getObjectManager()->get(TransportBuilderMock::class);
66-
$this->tokenManager = Bootstrap::getObjectManager()->get(UserConfigTokenManagerInterface::class);
67-
$this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class);
67+
$this->tfa = BootstrapHelper::getObjectManager()->get(TfaInterface::class);
68+
$this->transportBuilderMock = BootstrapHelper::getObjectManager()->get(TransportBuilderMock::class);
69+
$this->tokenManager = BootstrapHelper::getObjectManager()->get(UserConfigTokenManagerInterface::class);
70+
$this->aclBuilder = BootstrapHelper::getObjectManager()->get(Builder::class);
6871

69-
$this->manager = Bootstrap::getObjectManager()->get(UserConfigRequestManagerInterface::class);
72+
$this->manager = BootstrapHelper::getObjectManager()->get(UserConfigRequestManagerInterface::class);
7073
}
7174

7275
/**
@@ -104,25 +107,28 @@ public function testIsRequiredWithConfig(): void
104107
}
105108

106109
/**
107-
* Check that app config request E-mail is NOT sent for a user that does not posses proper rights.
110+
* Check that app config request E-mail is NOT sent for a user that does not possess proper rights.
108111
*
109112
* @return void
110-
* @throws \Throwable
113+
* @throws Throwable
111114
* @magentoAppArea adminhtml
112115
* @magentoAppIsolation enabled
113116
*/
114117
public function testFailAppConfigRequest(): void
115118
{
116-
$this->expectException(\Magento\Framework\Exception\AuthorizationException::class);
117-
$this->aclBuilder->getAcl()->deny(null, 'Magento_TwoFactorAuth::config');
119+
$this->expectException(AuthorizationException::class);
120+
$this->aclBuilder->getAcl()->deny(
121+
Bootstrap::ADMIN_ROLE_ID,
122+
'Magento_TwoFactorAuth::config'
123+
);
118124
$this->manager->sendConfigRequestTo($this->user);
119125
}
120126

121127
/**
122128
* Check that app config request E-mail is sent for a user that posseses proper rights.
123129
*
124130
* @return void
125-
* @throws \Throwable
131+
* @throws Throwable
126132
* @magentoAppArea adminhtml
127133
*/
128134
public function testSendAppConfigRequest(): void
@@ -151,7 +157,7 @@ public function testSendAppConfigRequest(): void
151157
* Check that personal 2FA config request E-mail is sent for users.
152158
*
153159
* @return void
154-
* @throws \Throwable
160+
* @throws Throwable
155161
* @magentoAppArea adminhtml
156162
* @magentoConfigFixture default/twofactorauth/general/force_providers google
157163
*/

0 commit comments

Comments
 (0)