Skip to content

Commit be825c3

Browse files
authored
Merge pull request #89 from /issues/86-migration
security-package/issues/101: Add ReCaptchaMigration module
2 parents 54d9363 + 97f868e commit be825c3

File tree

6 files changed

+311
-0
lines changed

6 files changed

+311
-0
lines changed

ReCaptchaMigration/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Please refer to: https://github.com/magento/security-package
Lines changed: 267 additions & 0 deletions
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+
}

ReCaptchaMigration/composer.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "magento/module-re-captcha-migration",
3+
"description": "Google reCAPTCHA config migration for Magento2",
4+
"require": {
5+
"php": "~7.1.3||~7.2.0||~7.3.0",
6+
"magento/framework": "*",
7+
"magento/module-config": "*"
8+
},
9+
"type": "magento2-module",
10+
"license": "OSL-3.0",
11+
"autoload": {
12+
"files": [
13+
"registration.php"
14+
],
15+
"psr-4": {
16+
"Magento\\ReCaptchaMigration\\": ""
17+
}
18+
}
19+
}

ReCaptchaMigration/etc/module.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
10+
<module name="Magento_ReCaptchaMigration"/>
11+
</config>

ReCaptchaMigration/registration.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
\Magento\Framework\Component\ComponentRegistrar::register(
9+
\Magento\Framework\Component\ComponentRegistrar::MODULE,
10+
'Magento_ReCaptchaMigration',
11+
__DIR__
12+
);

_metapackage/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"magento/module-re-captcha-contact": "*",
2929
"magento/module-re-captcha-customer": "*",
3030
"magento/module-re-captcha-frontend-ui": "*",
31+
"magento/module-re-captcha-migration": "*",
3132
"magento/module-re-captcha-newsletter": "*",
3233
"magento/module-re-captcha-paypal": "*",
3334
"magento/module-re-captcha-review": "*",

0 commit comments

Comments
 (0)