From fb7694d767f47df4945d2ada078bbeaa3f708264 Mon Sep 17 00:00:00 2001 From: ADmad Date: Tue, 28 Jan 2025 13:18:33 +0530 Subject: [PATCH 1/8] Cleanup the component. - Set the helper in beforeRener() - Properly handle exception for api call --- .../Component/RecaptchaComponent.php | 23 ++++++++++++++++--- .../Component/RecaptchaComponentTest.php | 8 +++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Controller/Component/RecaptchaComponent.php b/src/Controller/Component/RecaptchaComponent.php index 7a8c675..fd0592e 100644 --- a/src/Controller/Component/RecaptchaComponent.php +++ b/src/Controller/Component/RecaptchaComponent.php @@ -5,13 +5,17 @@ use Cake\Controller\Component; use Cake\Core\Configure; +use Cake\Event\EventInterface; use Cake\Http\Client; +use Exception; /** * Recaptcha component */ class RecaptchaComponent extends Component { + public const VERIFY_ENDPOINT = 'https://www.google.com/recaptcha/api/siteverify'; + /** * Default config * @@ -39,10 +43,19 @@ class RecaptchaComponent extends Component */ public function initialize(array $config = []): void { - if (empty($config)) { + if ($config === []) { $this->setConfig(Configure::read('Recaptcha', [])); } + } + /** + * beforeRender + * + * @param \Cake\Event\EventInterface $event Controller.beforeRender event + * @return void + */ + public function beforeRender(EventInterface $event): void + { $this->getController()->viewBuilder()->addHelpers(['Recaptcha.Recaptcha' => $this->_config]); } @@ -59,7 +72,11 @@ public function verify(): bool $controller = $this->_registry->getController(); if ($controller->getRequest()->getData('g-recaptcha-response')) { - $response = json_decode($this->apiCall()); + try { + $response = json_decode($this->apiCall(), flags: JSON_THROW_ON_ERROR); + } catch (Exception $e) { + return false; + } if (isset($response->success)) { return (bool)$response->success; @@ -85,6 +102,6 @@ protected function apiCall(): string 'remoteip' => $controller->getRequest()->clientIp(), ]; - return (string)$client->post('https://www.google.com/recaptcha/api/siteverify', $data)->getBody(); + return (string)$client->post(static::VERIFY_ENDPOINT, $data)->getBody(); } } diff --git a/tests/TestCase/Controller/Component/RecaptchaComponentTest.php b/tests/TestCase/Controller/Component/RecaptchaComponentTest.php index 5e82573..e91ca81 100644 --- a/tests/TestCase/Controller/Component/RecaptchaComponentTest.php +++ b/tests/TestCase/Controller/Component/RecaptchaComponentTest.php @@ -5,6 +5,7 @@ use Cake\Controller\ComponentRegistry; use Cake\Controller\Controller; +use Cake\Event\Event; use Cake\Http\ServerRequest as Request; use Cake\TestSuite\TestCase; use Recaptcha\Controller\Component\RecaptchaComponent; @@ -38,6 +39,13 @@ public function setUp(): void ->getMock(); } + public function testBeforeRender(): void + { + $this->Recaptcha->beforeRender(new Event('Controller.beforeRender')); + $helpers = $this->controller->viewBuilder()->getHelpers(); + $this->assertArrayHasKey('Recaptcha', $helpers); + } + public function testVerifyFalse(): void { $this->assertFalse($this->Recaptcha->verify()); From 9bed143378260f22817272c4515bb4d170f40def Mon Sep 17 00:00:00 2001 From: ADmad Date: Tue, 28 Jan 2025 13:24:52 +0530 Subject: [PATCH 2/8] Merge global config --- src/Controller/Component/RecaptchaComponent.php | 5 ++--- src/View/Helper/RecaptchaHelper.php | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Controller/Component/RecaptchaComponent.php b/src/Controller/Component/RecaptchaComponent.php index fd0592e..4a514c8 100644 --- a/src/Controller/Component/RecaptchaComponent.php +++ b/src/Controller/Component/RecaptchaComponent.php @@ -43,9 +43,8 @@ class RecaptchaComponent extends Component */ public function initialize(array $config = []): void { - if ($config === []) { - $this->setConfig(Configure::read('Recaptcha', [])); - } + $config += Configure::read('Recaptcha', []); + $this->setConfig($config, merge: false); } /** diff --git a/src/View/Helper/RecaptchaHelper.php b/src/View/Helper/RecaptchaHelper.php index f4e78e5..264fc8a 100644 --- a/src/View/Helper/RecaptchaHelper.php +++ b/src/View/Helper/RecaptchaHelper.php @@ -3,6 +3,7 @@ namespace Recaptcha\View\Helper; +use Cake\Core\Configure; use Cake\View\Helper; /** @@ -10,15 +11,27 @@ */ class RecaptchaHelper extends Helper { + /** + * initialize + * + * @param array $config config + * @return void + */ + public function initialize(array $config = []): void + { + $config += Configure::read('Recaptcha', []); + $this->setConfig($config, merge: false); + } + /** * Display recaptcha function * * @return string */ - public function display() + public function display(): string { $recaptcha = $this->getConfig(); - if (!$recaptcha['enable']) { + if (empty($recaptcha['enable'])) { return ''; } From 5dc5906bb32c7bf1abb5a38a94c7dc9be01bce8b Mon Sep 17 00:00:00 2001 From: ADmad Date: Tue, 28 Jan 2025 13:26:13 +0530 Subject: [PATCH 3/8] disable uneeded hooks --- src/RecaptchaPlugin.php | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/RecaptchaPlugin.php b/src/RecaptchaPlugin.php index 66c5862..51e79f8 100644 --- a/src/RecaptchaPlugin.php +++ b/src/RecaptchaPlugin.php @@ -14,16 +14,43 @@ class RecaptchaPlugin extends BasePlugin { /** * Do bootstrapping or not + * + * @var bool */ protected bool $bootstrapEnabled = false; + /** + * Console middleware + * + * @var bool + */ + protected bool $consoleEnabled = false; + + /** + * Enable middleware + * + * @var bool + */ + protected bool $middlewareEnabled = false; + + /** + * Register container services + * + * @var bool + */ + protected bool $servicesEnabled = false; + /** * Load routes or not + * + * @var bool */ protected bool $routesEnabled = false; /** - * Console middleware + * Load events or not + * + * @var bool */ - protected bool $consoleEnabled = false; + protected bool $eventsEnabled = false; } From 5aaf43f36a1db63b9365d76ad9d388948a6754a1 Mon Sep 17 00:00:00 2001 From: ADmad Date: Tue, 28 Jan 2025 13:57:10 +0530 Subject: [PATCH 4/8] Update the helper and template. - Better defualts and config merging - Config overriding when calling display() - `callback` option added - Cleaned up uneeded JS code - Script block is now used by default - The `g-recaptcha-response` field populated by Recaptcha is unlock for form protector --- README.md | 14 ++++---- .../Component/RecaptchaComponent.php | 2 ++ src/View/Helper/RecaptchaHelper.php | 32 +++++++++++++++++-- templates/element/recaptcha.php | 32 +++++++++++++------ .../View/Helper/RecaptchaHelperTest.php | 8 +++-- 5 files changed, 68 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index fbab33b..4216dd2 100644 --- a/README.md +++ b/README.md @@ -34,22 +34,22 @@ $this->loadComponent('Recaptcha.Recaptcha', [ 'secret' => 'your_secret', 'type' => 'image', // image/audio 'theme' => 'light', // light/dark - 'lang' => 'vi', // default en + 'lang' => 'en', // default 'en' 'size' => 'normal' // normal/compact + 'callback' => null, // `callback` data attribute for the recaptcha div, default `null` + 'scriptBlock' => true // Value for `block` option for HtmlHelper::script() call ]); ``` -Override default configure from app config file: +Override default config from app config file: ```php // file: config/app.php /** * Recaptcha configuration. - * */ 'Recaptcha' => [ - 'enable' => true, 'sitekey' => 'your_site_key', 'secret' => 'your_secret', 'type' => 'image', @@ -62,7 +62,7 @@ Override default configure from app config file: Override default configure from recaptcha config file: ```php -// ffile: config/recaptcha.php +// file: config/recaptcha.php return [ /** @@ -96,12 +96,12 @@ Config preference: ## Usage -Display recaptcha in your view: +Display recaptcha in your template: ```php Form->create() ?> Form->control('email') ?> - // Display recaptcha box in your view, if configure has enable = false, nothing will be displayed + // Display recaptcha box in your template, if configure has enable = false, nothing will be displayed Recaptcha->display() ?> Form->button() ?> Form->end() ?> diff --git a/src/Controller/Component/RecaptchaComponent.php b/src/Controller/Component/RecaptchaComponent.php index 4a514c8..86f1651 100644 --- a/src/Controller/Component/RecaptchaComponent.php +++ b/src/Controller/Component/RecaptchaComponent.php @@ -29,10 +29,12 @@ class RecaptchaComponent extends Component 'secret' => '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe', 'theme' => 'light', 'type' => 'image', + 'callback' => null, 'enable' => true, 'lang' => 'en', 'size' => 'normal', 'httpClientOptions' => [], + 'scriptBlock' => true, ]; /** diff --git a/src/View/Helper/RecaptchaHelper.php b/src/View/Helper/RecaptchaHelper.php index 264fc8a..de67f6b 100644 --- a/src/View/Helper/RecaptchaHelper.php +++ b/src/View/Helper/RecaptchaHelper.php @@ -11,6 +11,33 @@ */ class RecaptchaHelper extends Helper { + /** + * Default config + * + * These are merged with user-provided config when the component is used. + * + * @var array + */ + protected array $_defaultConfig = [ + // This is test only key/secret + 'sitekey' => '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI', + 'secret' => '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe', + 'theme' => 'light', + 'type' => 'image', + 'callback' => null, + 'enable' => true, + 'lang' => 'en', + 'size' => 'normal', + 'scriptBlock' => true, + ]; + + /** + * Helpers + * + * @var array + */ + protected array $helpers = ['Form']; + /** * initialize * @@ -26,11 +53,12 @@ public function initialize(array $config = []): void /** * Display recaptcha function * + * @param array $config Config * @return string */ - public function display(): string + public function display(array $config = []): string { - $recaptcha = $this->getConfig(); + $recaptcha = $config + $this->getConfig(); if (empty($recaptcha['enable'])) { return ''; } diff --git a/templates/element/recaptcha.php b/templates/element/recaptcha.php index 393a3f9..ffc15f7 100644 --- a/templates/element/recaptcha.php +++ b/templates/element/recaptcha.php @@ -1,19 +1,33 @@ -Html->script('https://www.google.com/recaptcha/api.js?hl=' . $recaptcha['lang'] . '&onload=CaptchaCallback&render=explicit') ?> - +Form->unlockField('g-recaptcha-response'); +} catch (CakeException) { + // If FormProtectorComponent is not loaded, an exception in thrown in older CakePHP versions. +} +?> +Html->script( + 'https://www.google.com/recaptcha/api.js?hl=' . $recaptcha['lang'], + [ + 'block' => $recaptcha['scriptBlock'], + 'async' => true, + 'defer' => true, + ] +) ?>
+ + data-callback="" + +>