Skip to content

Commit 854bf80

Browse files
Merge branch '1.0-develop' into security-package/try-catch-for-handler
2 parents e4caaaf + be825c3 commit 854bf80

File tree

33 files changed

+867
-27
lines changed

33 files changed

+867
-27
lines changed

ReCaptchaAdminUi/etc/di.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
99
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
10-
<virtualType name="Magento\ReCaptchaApi\Model\OptionSource\Type"
10+
<virtualType name="Magento\ReCaptchaAdminUi\Model\OptionSource\Type"
1111
type="Magento\ReCaptchaAdminUi\Model\OptionSource">
1212
<arguments>
1313
<argument name="options" xsi:type="array">

ReCaptchaCheckout/view/frontend/layout/checkout_index_index.xml

-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
<item name="displayArea" xsi:type="string">additional-login-form-fields</item>
2929
<item name="configSource" xsi:type="string">checkoutConfig</item>
3030
<item name="reCaptchaId" xsi:type="string">recaptcha-checkout-inline-login</item>
31-
<item name="zone" xsi:type="string">login</item>
3231
</item>
3332
</item>
3433
</item>
@@ -47,7 +46,6 @@
4746
<item name="displayArea" xsi:type="string">additional-login-form-fields</item>
4847
<item name="configSource" xsi:type="string">checkoutConfig</item>
4948
<item name="reCaptchaId" xsi:type="string">recaptcha-checkout-inline-login-billing</item>
50-
<item name="zone" xsi:type="string">login</item>
5149
</item>
5250
</item>
5351
</item>

ReCaptchaContact/etc/adminhtml/system.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<field id="contact" translate="label" type="select" sortOrder="140" showInDefault="1"
1414
showInWebsite="1" showInStore="0" canRestore="1">
1515
<label>Enable for Contact Us</label>
16-
<source_model>Magento\ReCaptchaApi\Model\OptionSource\Type</source_model>
16+
<source_model>Magento\ReCaptchaAdminUi\Model\OptionSource\Type</source_model>
1717
</field>
1818
</group>
1919
</section>

ReCaptchaCustomer/etc/adminhtml/system.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
<field id="customer_login" translate="label" type="select" sortOrder="110" showInDefault="1"
1414
showInWebsite="1" showInStore="0" canRestore="1">
1515
<label>Enable for Customer Login</label>
16-
<source_model>Magento\ReCaptchaApi\Model\OptionSource\Type</source_model>
16+
<source_model>Magento\ReCaptchaAdminUi\Model\OptionSource\Type</source_model>
1717
</field>
1818
<field id="customer_forgot_password" translate="label" type="select" sortOrder="120" showInDefault="1"
1919
showInWebsite="1" showInStore="0" canRestore="1">
2020
<label>Enable for Forgot Password</label>
21-
<source_model>Magento\ReCaptchaApi\Model\OptionSource\Type</source_model>
21+
<source_model>Magento\ReCaptchaAdminUi\Model\OptionSource\Type</source_model>
2222
</field>
2323
<field id="customer_create" translate="label" type="select" sortOrder="130" showInDefault="1"
2424
showInWebsite="1" showInStore="0" canRestore="1">
2525
<label>Enable for Create New Customer Account</label>
26-
<source_model>Magento\ReCaptchaApi\Model\OptionSource\Type</source_model>
26+
<source_model>Magento\ReCaptchaAdminUi\Model\OptionSource\Type</source_model>
2727
</field>
2828
</group>
2929
</section>

ReCaptchaFrontendUi/view/frontend/web/js/reCaptcha.js

+23-12
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ define(
8989
$parentForm,
9090
$wrapper,
9191
$reCaptcha,
92-
widgetId,
93-
listeners;
92+
widgetId;
9493

9594
if (this.captchaInitialized) {
9695
return;
@@ -129,9 +128,26 @@ define(
129128

130129
// eslint-disable-next-line no-undef
131130
widgetId = grecaptcha.render(this.getReCaptchaId(), parameters);
131+
this.initParentForm($parentForm, widgetId);
132+
133+
registry.ids.push(this.getReCaptchaId());
134+
registry.captchaList.push(widgetId);
135+
registry.tokenFields.push(this.tokenField);
132136

133-
if (this.getIsInvisibleRecaptcha() && $parentForm.length > 0) {
134-
$parentForm.submit(function (event) {
137+
},
138+
139+
/**
140+
* Initialize parent form.
141+
*
142+
* @param {Object} parentForm
143+
* @param {String} widgetId
144+
*/
145+
initParentForm: function (parentForm, widgetId) {
146+
var me = this,
147+
listeners;
148+
149+
if (this.getIsInvisibleRecaptcha() && parentForm.length > 0) {
150+
parentForm.submit(function (event) {
135151
if (!me.tokenField.value) {
136152
// eslint-disable-next-line no-undef
137153
grecaptcha.execute(widgetId);
@@ -141,21 +157,16 @@ define(
141157
});
142158

143159
// Move our (last) handler topmost. We need this to avoid submit bindings with ko.
144-
listeners = $._data($parentForm[0], 'events').submit;
160+
listeners = $._data(parentForm[0], 'events').submit;
145161
listeners.unshift(listeners.pop());
146162

147163
// Create a virtual token field
148164
this.tokenField = $('<input type="text" name="token" style="display: none" />')[0];
149-
this.$parentForm = $parentForm;
150-
$parentForm.append(this.tokenField);
165+
this.$parentForm = parentForm;
166+
parentForm.append(this.tokenField);
151167
} else {
152168
this.tokenField = null;
153169
}
154-
155-
registry.ids.push(this.getReCaptchaId());
156-
registry.captchaList.push(widgetId);
157-
registry.tokenFields.push(this.tokenField);
158-
159170
},
160171

161172
validateReCaptcha: function (state) {

ReCaptchaMigration/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Please refer to: https://github.com/magento/security-package
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\ReCaptchaMigration\Setup\Patch\Data;
9+
10+
use Magento\Framework\App\Config\ScopeConfigInterface;
11+
use Magento\Framework\App\Config\Storage\WriterInterface;
12+
use Magento\Framework\Encryption\EncryptorInterface;
13+
use Magento\Framework\Setup\ModuleDataSetupInterface;
14+
use Magento\Framework\Setup\Patch\DataPatchInterface;
15+
use Magento\Framework\Setup\Patch\PatchVersionInterface;
16+
17+
/**
18+
* Migrate config from frontend and backend scopes to reCAPTCHA modules.
19+
*/
20+
class MigrateConfigToRecaptchaModules implements DataPatchInterface, PatchVersionInterface
21+
{
22+
/**
23+
* @var ModuleDataSetupInterface
24+
*/
25+
private $moduleDataSetup;
26+
27+
/**
28+
* @var ScopeConfigInterface
29+
*/
30+
private $scopeConfig;
31+
32+
/**
33+
* @var WriterInterface
34+
*/
35+
private $writer;
36+
37+
/**
38+
* @var EncryptorInterface
39+
*/
40+
private $encryptor;
41+
42+
/**
43+
* @param ModuleDataSetupInterface $moduleDataSetup
44+
* @param ScopeConfigInterface $scopeConfig
45+
* @param WriterInterface $writer
46+
* @param EncryptorInterface $encryptor
47+
*/
48+
public function __construct(
49+
ModuleDataSetupInterface $moduleDataSetup,
50+
ScopeConfigInterface $scopeConfig,
51+
WriterInterface $writer,
52+
EncryptorInterface $encryptor
53+
) {
54+
$this->moduleDataSetup = $moduleDataSetup;
55+
$this->scopeConfig = $scopeConfig;
56+
$this->writer = $writer;
57+
$this->encryptor = $encryptor;
58+
}
59+
60+
/**
61+
* @inheritdoc
62+
*/
63+
public function apply()
64+
{
65+
$scopes = ['frontend', 'backend'];
66+
foreach ($scopes as $scope) {
67+
$this->copyRecaptchaKeys($scope);
68+
$this->copyModuleSpecificRecords($scope);
69+
$this->copyEnabledRecaptcha($scope);
70+
$this->disableLegacyRecaptcha($scope);
71+
}
72+
}
73+
74+
/**
75+
* Copy 'enabled' reCAPTCHA.
76+
*
77+
* @param string $scope
78+
*/
79+
private function copyEnabledRecaptcha(string $scope): void
80+
{
81+
$type = $this->getActiveRecaptchaType();
82+
if (!$type) {
83+
return;
84+
}
85+
86+
$availableRecaptchaPreferences = $this->getAvailableRecaptchaPreferences();
87+
foreach ($availableRecaptchaPreferences[$scope] as $availablePreference => $legacyPreference) {
88+
$availableRecaptchaPreferencePath = "recaptcha_$scope/type_for/$availablePreference";
89+
$recaptchaPreferenceEnabled = $this->scopeConfig->getValue($availableRecaptchaPreferencePath);
90+
$recaptchaPreferenceEnabledLegacy = $this->scopeConfig->getValue(
91+
"msp_securitysuite_recaptcha/$scope/enabled$legacyPreference"
92+
);
93+
if (null === $recaptchaPreferenceEnabled && '0' !== $recaptchaPreferenceEnabledLegacy) {
94+
$this->writer->save($availableRecaptchaPreferencePath, $type);
95+
}
96+
}
97+
}
98+
99+
/**
100+
* Disable legacy reCAPTCHA module to prevent multiple widget rendering.
101+
*
102+
* @param string $scope
103+
*/
104+
private function disableLegacyRecaptcha(string $scope): void
105+
{
106+
$this->writer->save("msp_securitysuite_recaptcha/$scope/enabled", 0);
107+
}
108+
109+
/**
110+
* Copy reCAPTCHA keys.
111+
*
112+
* @param string $scope
113+
*/
114+
private function copyRecaptchaKeys(string $scope): void
115+
{
116+
$type = $this->getActiveRecaptchaType();
117+
if ($type) {
118+
$this->copyRecord(
119+
"msp_securitysuite_recaptcha/general/public_key",
120+
"recaptcha_$scope/type_$type/public_key"
121+
);
122+
$privateKey = $this->scopeConfig->getValue(
123+
"recaptcha_$scope/type_$type/private_key"
124+
);
125+
$privateKeyLegacy = $this->scopeConfig->getValue(
126+
'msp_securitysuite_recaptcha/general/private_key'
127+
);
128+
if (!$privateKey && $privateKeyLegacy) {
129+
$privateKeyEncrypted = $this->encryptor->encrypt($privateKeyLegacy);
130+
$this->writer->save("recaptcha_$scope/type_$type/private_key", $privateKeyEncrypted);
131+
}
132+
}
133+
}
134+
135+
/**
136+
* Copy module-specific records.
137+
*
138+
* @param string $scope
139+
*/
140+
private function copyModuleSpecificRecords(string $scope): void
141+
{
142+
foreach ($this->getModuleSpecificRecords() as $module => $specificRecords) {
143+
foreach ($specificRecords as $actualRecord => $legacyRecord) {
144+
$this->copyRecord(
145+
"msp_securitysuite_recaptcha/$scope/$legacyRecord",
146+
"recaptcha_$scope/type_$module/$actualRecord"
147+
);
148+
}
149+
}
150+
}
151+
152+
/**
153+
* Get module-specific records.
154+
*
155+
* @return array
156+
*/
157+
private function getModuleSpecificRecords(): array
158+
{
159+
return [
160+
'recaptcha' => [
161+
'theme' => 'theme',
162+
'lang' => 'lang',
163+
'size' => 'size'
164+
],
165+
'invisible' => [
166+
'theme' => 'theme',
167+
'lang' => 'lang',
168+
'position' => 'position'
169+
],
170+
'recaptcha_v3' => [
171+
'theme' => 'theme',
172+
'lang' => 'lang',
173+
'score_threshold' => 'min_score',
174+
'position' => 'position'],
175+
];
176+
}
177+
178+
/**
179+
* Get available recaptcha preferences.
180+
*
181+
* @return array
182+
*/
183+
private function getAvailableRecaptchaPreferences(): array
184+
{
185+
return [
186+
'frontend' => [
187+
'customer_login' => '_login',
188+
'customer_forgot_password' => '_forgot',
189+
'customer_create' => '_create',
190+
'contact' => '_contact',
191+
'product_review' => '_review',
192+
'newsletter' => '_newsletter',
193+
'sendfriend' => '_sendfriend',
194+
],
195+
'backend' => [
196+
'user_login' => '',
197+
'user_forgot_password' => '',
198+
],
199+
];
200+
}
201+
202+
/**
203+
* Get active reCAPTCHA type from config (recaptcha/scope/type).
204+
*
205+
* @return string|null
206+
*/
207+
private function getActiveRecaptchaType(): ?string
208+
{
209+
return $this->scopeConfig->getValue('msp_securitysuite_recaptcha/general/type');
210+
}
211+
212+
/**
213+
* Copy one config record.
214+
*
215+
* Skip if record on destination path already exists.
216+
*
217+
* @param string $srcPath
218+
* @param string $dstPath
219+
*/
220+
private function copyRecord(string $srcPath, string $dstPath): void
221+
{
222+
$connection = $this->moduleDataSetup->getConnection();
223+
$configDataTable = $this->moduleDataSetup->getTable('core_config_data');
224+
225+
$dstSelect = $connection->select()
226+
->from($configDataTable)
227+
->where('path = ?', $dstPath);
228+
229+
if (!$connection->fetchOne($dstSelect)) {
230+
$srcSelect = $connection->select()
231+
->from($configDataTable)
232+
->where('path = ?', $srcPath);
233+
234+
$rows = $connection->fetchAll($srcSelect);
235+
foreach ($rows as $row) {
236+
unset($row['config_id']);
237+
$row['path'] = $dstPath;
238+
239+
$connection->insert($configDataTable, $row);
240+
}
241+
}
242+
}
243+
244+
/**
245+
* @inheritdoc
246+
*/
247+
public static function getDependencies()
248+
{
249+
return [];
250+
}
251+
252+
/**
253+
* @inheritdoc
254+
*/
255+
public static function getVersion()
256+
{
257+
return '3.0.0';
258+
}
259+
260+
/**
261+
* @inheritdoc
262+
*/
263+
public function getAliases()
264+
{
265+
return [];
266+
}
267+
}

0 commit comments

Comments
 (0)