From c2c3078f6dc1ebf965b74b9155c9c5c1b0673665 Mon Sep 17 00:00:00 2001 From: Melvin Achterhuis Date: Wed, 28 Sep 2022 20:52:59 +0200 Subject: [PATCH] Initial commit --- README.md | 26 ++++++ composer.json | 28 ++++++ src/MelvTurnstile.php | 9 ++ ...Migration1664374217addTurnStileCaptcha.php | 80 ++++++++++++++++++ src/Resources/app/administration/src/main.js | 1 + .../sw-settings-captcha-select-v2/index.js | 12 +++ .../snippet/de-DE.json | 12 +++ .../snippet/en-GB.json | 12 +++ .../sw-settings-captcha-select-v2.html.twig | 36 ++++++++ src/Resources/config/plugin.png | Bin 0 -> 5238 bytes src/Resources/config/services.xml | 13 +++ .../administration/js/melv-turnstile.js | 1 + src/Resources/snippet/storefront.de-DE.json | 7 ++ src/Resources/snippet/storefront.en-GB.json | 7 ++ .../captcha/cloudFlareTurnstile.html.twig | 9 ++ .../storefront/component/recaptcha.html.twig | 11 +++ .../Framework/Captcha/CloudFlareTurnstile.php | 77 +++++++++++++++++ 17 files changed, 341 insertions(+) create mode 100644 README.md create mode 100644 composer.json create mode 100644 src/MelvTurnstile.php create mode 100644 src/Migration/Migration1664374217addTurnStileCaptcha.php create mode 100644 src/Resources/app/administration/src/main.js create mode 100644 src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/index.js create mode 100644 src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/de-DE.json create mode 100644 src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/en-GB.json create mode 100644 src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/sw-settings-captcha-select-v2.html.twig create mode 100644 src/Resources/config/plugin.png create mode 100644 src/Resources/config/services.xml create mode 100644 src/Resources/public/administration/js/melv-turnstile.js create mode 100644 src/Resources/snippet/storefront.de-DE.json create mode 100644 src/Resources/snippet/storefront.en-GB.json create mode 100644 src/Resources/views/storefront/component/captcha/cloudFlareTurnstile.html.twig create mode 100644 src/Resources/views/storefront/component/recaptcha.html.twig create mode 100644 src/Storefront/Framework/Captcha/CloudFlareTurnstile.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..3701ec3 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +## A plugin for [Shopware 6](https://github.com/shopware/platform) + +**Proof-of-concept**: Integrates CloudFlare Turnstile with Shopware 6. + +Create a free account to claim your keys: https://www.cloudflare.com/en-gb/lp/turnstile/ + +*Config:* +![](https://i.imgur.com/qutsRPd.png) + +Known issues: +* Not working when form in modal +* No alert when captcha invalid +* Missing translations for German +* .. + +## Requirements + +| Version | Requirements | +|------------|---------------------------- | +| 0.1.0 | Shopware 6.4 >= | + +## License + +Plugin's Icon by [flaticon](https://www.flaticon.com). + +The plugin is released under MIT. For a full overview check the [LICENSE](./LICENSE) file. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..df44615 --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "melvinachterhuis/turnstile-shopware6-plugin", + "description": "CloudFlare Turnstile Captcha", + "version": "0.1.0", + "type": "shopware-platform-plugin", + "license": "MIT", + "authors": [ + { + "name": "Melvin Achterhuis" + } + ], + "require": { + "shopware/core": "6.4.*", + "ext-curl": "*" + }, + "autoload": { + "psr-4": { + "Melv\\Turnstile\\": "src/" + } + }, + "extra": { + "shopware-plugin-class": "Melv\\Turnstile\\MelvTurnstile", + "label": { + "de-DE": "CloudFlare Turnstile Captcha", + "en-GB": "CloudFlare Turnstile Captcha" + } + } +} diff --git a/src/MelvTurnstile.php b/src/MelvTurnstile.php new file mode 100644 index 0000000..4d5d712 --- /dev/null +++ b/src/MelvTurnstile.php @@ -0,0 +1,9 @@ + [ + 'name' => 'Honeypot', + 'isActive' => false, + ], + 'basicCaptcha' => [ + 'name' => 'basicCaptcha', + 'isActive' => false, + ], + 'googleReCaptchaV2' => [ + 'name' => 'googleReCaptchaV2', + 'isActive' => false, + 'config' => [ + 'siteKey' => '', + 'secretKey' => '', + 'invisible' => false, + ], + ], + 'googleReCaptchaV3' => [ + 'name' => 'googleReCaptchaV3', + 'isActive' => false, + 'config' => [ + 'siteKey' => '', + 'secretKey' => '', + 'thresholdScore' => 0.5, + ], + ], + 'cloudFlareTurnstile' => [ + 'name' => 'cloudFlareTurnstile', + 'isActive' => false, + 'config' => [ + 'siteKey' => '', + 'secretKey' => '' + ] + ] + ]; + + + public function getCreationTimestamp(): int + { + return 1664374217; + } + + public function update(Connection $connection): void + { + //TODO: Can we prevent overriding current CAPTCHA settings? + $configId = $connection->fetchColumn('SELECT id FROM system_config WHERE configuration_key = :key AND updated_at IS NULL', [ + 'key' => self::CONFIG_KEY, + ]); + + if (!$configId) { + return; + } + + $connection->update('system_config', [ + 'configuration_key' => self::CONFIG_KEY, + 'configuration_value' => json_encode(['_value' => $this->captchaItems]), + 'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT), + ], [ + 'id' => $configId, + ]); + } + + public function updateDestructive(Connection $connection): void + { + // implement update destructive + } +} diff --git a/src/Resources/app/administration/src/main.js b/src/Resources/app/administration/src/main.js new file mode 100644 index 0000000..e53e55e --- /dev/null +++ b/src/Resources/app/administration/src/main.js @@ -0,0 +1 @@ +import './module/sw-settings-basic-information/component/sw-settings-captcha-select-v2'; \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/index.js b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/index.js new file mode 100644 index 0000000..a27ac0b --- /dev/null +++ b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/index.js @@ -0,0 +1,12 @@ +import template from './sw-settings-captcha-select-v2.html.twig'; +import enGB from './snippet/en-GB.json'; +import deDE from './snippet/de-DE.json'; + +const { Component, Locale } = Shopware; + +Locale.extend('en-GB', enGB); +Locale.extend('de-DE', deDE); + +Component.override('sw-settings-captcha-select-v2', { + template, +}); \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/de-DE.json b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/de-DE.json new file mode 100644 index 0000000..40bd863 --- /dev/null +++ b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/de-DE.json @@ -0,0 +1,12 @@ +{ + "sw-settings-basic-information": { + "captcha": { + "label": { + "cloudFlareTurnstile": "CloudFlare Turnstile", + "cloudFlareTurnstileSiteKey": "CloudFlare Turnstile site key", + "cloudFlareTurnstileSecretKey": "CloudFlare Turnstile secret key", + "cloudFlareTurnstileDescription": "Turnstile is CloudFlare's CAPTCHA alternative. It automatically chooses from a rotating suite of non-intrusive browser challenges based on telemetry and client behavior exhibited during a session." + } + } + } +} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/en-GB.json b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/en-GB.json new file mode 100644 index 0000000..40bd863 --- /dev/null +++ b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/snippet/en-GB.json @@ -0,0 +1,12 @@ +{ + "sw-settings-basic-information": { + "captcha": { + "label": { + "cloudFlareTurnstile": "CloudFlare Turnstile", + "cloudFlareTurnstileSiteKey": "CloudFlare Turnstile site key", + "cloudFlareTurnstileSecretKey": "CloudFlare Turnstile secret key", + "cloudFlareTurnstileDescription": "Turnstile is CloudFlare's CAPTCHA alternative. It automatically chooses from a rotating suite of non-intrusive browser challenges based on telemetry and client behavior exhibited during a session." + } + } + } +} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/sw-settings-captcha-select-v2.html.twig b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/sw-settings-captcha-select-v2.html.twig new file mode 100644 index 0000000..faa021f --- /dev/null +++ b/src/Resources/app/administration/src/module/sw-settings-basic-information/component/sw-settings-captcha-select-v2/sw-settings-captcha-select-v2.html.twig @@ -0,0 +1,36 @@ + +{% block sw_settings_captcha_select_v2_google_recaptcha_v2 %} + {% parent() %} + {% block sw_settings_captcha_select_v2_cloudflare_turnstile %} + + + + {% block sw_settings_captcha_select_v2_cloudflare_turnstile_description %} +

+ {{ $tc('sw-settings-basic-information.captcha.label.cloudFlareTurnstileDescription') }} +

+ {% endblock %} + + + {% block sw_settings_captcha_select_v2_cloudflare_turnstile_site_key %} + + {% endblock %} + + + {% block sw_settings_captcha_select_v2_cloudflare_turnstile_secret_key %} + + {% endblock %} +
+ {% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/config/plugin.png b/src/Resources/config/plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..879cb7af6ce7943c34a35ab70399cab35fc17162 GIT binary patch literal 5238 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 literal 0 HcmV?d00001 diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml new file mode 100644 index 0000000..463bbf8 --- /dev/null +++ b/src/Resources/config/services.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/public/administration/js/melv-turnstile.js b/src/Resources/public/administration/js/melv-turnstile.js new file mode 100644 index 0000000..8989788 --- /dev/null +++ b/src/Resources/public/administration/js/melv-turnstile.js @@ -0,0 +1 @@ +!function(e){var t={};function n(l){if(t[l])return t[l].exports;var r=t[l]={i:l,l:!1,exports:{}};return e[l].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,l){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:l})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var l=Object.create(null);if(n.r(l),Object.defineProperty(l,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(l,r,function(t){return e[t]}.bind(null,r));return l},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/bundles/melvturnstile/",n(n.s="wNoc")}({"+t2p":function(e){e.exports=JSON.parse('{"sw-settings-basic-information":{"captcha":{"label":{"cloudFlareTurnstile":"CloudFlare Turnstile","cloudFlareTurnstileSiteKey":"CloudFlare Turnstile site key","cloudFlareTurnstileSecretKey":"CloudFlare Turnstile secret key","cloudFlareTurnstileDescription":"Turnstile is CloudFlare\'s CAPTCHA alternative. It automatically chooses from a rotating suite of non-intrusive browser challenges based on telemetry and client behavior exhibited during a session."}}}}')},"R+yN":function(e){e.exports=JSON.parse('{"sw-settings-basic-information":{"captcha":{"label":{"cloudFlareTurnstile":"CloudFlare Turnstile","cloudFlareTurnstileSiteKey":"CloudFlare Turnstile site key","cloudFlareTurnstileSecretKey":"CloudFlare Turnstile secret key","cloudFlareTurnstileDescription":"Turnstile is CloudFlare\'s CAPTCHA alternative. It automatically chooses from a rotating suite of non-intrusive browser challenges based on telemetry and client behavior exhibited during a session."}}}}')},wNoc:function(e,t,n){"use strict";n.r(t);var l=n("+t2p"),r=n("R+yN"),s=Shopware,i=s.Component,c=s.Locale;c.extend("en-GB",l),c.extend("de-DE",r),i.override("sw-settings-captcha-select-v2",{template:'\n{% block sw_settings_captcha_select_v2_google_recaptcha_v2 %}\n {% parent() %}\n {% block sw_settings_captcha_select_v2_cloudflare_turnstile %}\n \n\n \n {% block sw_settings_captcha_select_v2_cloudflare_turnstile_description %}\n

\n {{ $tc(\'sw-settings-basic-information.captcha.label.cloudFlareTurnstileDescription\') }}\n

\n {% endblock %}\n\n \n {% block sw_settings_captcha_select_v2_cloudflare_turnstile_site_key %}\n \n {% endblock %}\n\n \n {% block sw_settings_captcha_select_v2_cloudflare_turnstile_secret_key %}\n \n {% endblock %}\n \n {% endblock %}\n{% endblock %}'})}}); \ No newline at end of file diff --git a/src/Resources/snippet/storefront.de-DE.json b/src/Resources/snippet/storefront.de-DE.json new file mode 100644 index 0000000..a585d78 --- /dev/null +++ b/src/Resources/snippet/storefront.de-DE.json @@ -0,0 +1,7 @@ +{ + "captcha": { + "cloudFlareTurnstile": { + "dataProtectionInformation": "This site is protected by Turnstile and the CloudFlare Privacy Policy and Terms of Service apply." + } + } +} \ No newline at end of file diff --git a/src/Resources/snippet/storefront.en-GB.json b/src/Resources/snippet/storefront.en-GB.json new file mode 100644 index 0000000..a585d78 --- /dev/null +++ b/src/Resources/snippet/storefront.en-GB.json @@ -0,0 +1,7 @@ +{ + "captcha": { + "cloudFlareTurnstile": { + "dataProtectionInformation": "This site is protected by Turnstile and the CloudFlare Privacy Policy and Terms of Service apply." + } + } +} \ No newline at end of file diff --git a/src/Resources/views/storefront/component/captcha/cloudFlareTurnstile.html.twig b/src/Resources/views/storefront/component/captcha/cloudFlareTurnstile.html.twig new file mode 100644 index 0000000..faf59a0 --- /dev/null +++ b/src/Resources/views/storefront/component/captcha/cloudFlareTurnstile.html.twig @@ -0,0 +1,9 @@ +{% block component_captcha_cloudflare_turnstile %} +
+ +
+ {{ "captcha.cloudFlareTurnstile.dataProtectionInformation"|trans|sw_sanitize }} +
+
+{% endblock %} diff --git a/src/Resources/views/storefront/component/recaptcha.html.twig b/src/Resources/views/storefront/component/recaptcha.html.twig new file mode 100644 index 0000000..19e187f --- /dev/null +++ b/src/Resources/views/storefront/component/recaptcha.html.twig @@ -0,0 +1,11 @@ +{% sw_extends '@Storefront/storefront/component/recaptcha.html.twig' %} + +{% block component_head_javascript_recaptcha %} + {{ parent() }} + {% block component_head_javascript_turnstile %} + {% set turnstileActive = config('core.basicInformation.activeCaptchasV2.cloudFlareTurnstile.isActive') %} + {% if turnstileActive %} + + {% endif %} + {% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Storefront/Framework/Captcha/CloudFlareTurnstile.php b/src/Storefront/Framework/Captcha/CloudFlareTurnstile.php new file mode 100644 index 0000000..f1bbb02 --- /dev/null +++ b/src/Storefront/Framework/Captcha/CloudFlareTurnstile.php @@ -0,0 +1,77 @@ +client = $client; + } + + /** + * {@inheritdoc} + */ + public function isValid(Request $request /* , array $captchaConfig */): bool + { + if (\func_num_args() < 2 || !\is_array(func_get_arg(1))) { + Feature::triggerDeprecationOrThrow( + 'v6.5.0.0', + 'Method `isValid()` in `CloudFlareTurnstile` expects passing the `$captchaConfig` as array as the second parameter in v6.5.0.0.' + ); + } + + if (!$request->get(self::CAPTCHA_REQUEST_PARAMETER)) { + return false; + } + + $captchaConfig = \func_get_args()[1] ?? []; + + $secretKey = !empty($captchaConfig['config']['secretKey']) ? $captchaConfig['config']['secretKey'] : null; + + if (!\is_string($secretKey)) { + return false; + } + + try { + $response = $this->client->request('POST', self::CLOUDFLARE_CAPTCHA_VERIFY_ENDPOINT, [ + 'form_params' => [ + 'secret' => $secretKey, + 'response' => $request->get(self::CAPTCHA_REQUEST_PARAMETER), + 'remoteip' => $request->getClientIp(), + ], + ]); + + $responseRaw = $response->getBody()->getContents(); + $response = json_decode($responseRaw, true); + + return $response && (bool) $response['success']; + } catch (ClientExceptionInterface $exception) { + return false; + } + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return self::CAPTCHA_NAME; + } +}