Skip to content

Commit

Permalink
Add support for reCaptcha v3
Browse files Browse the repository at this point in the history
  • Loading branch information
ajibarra committed Apr 19, 2024
1 parent 1d93a14 commit 4bb39ed
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 14 deletions.
1 change: 1 addition & 0 deletions Docs/Documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ and add this to your config/users.php file:
```php
'Users.reCaptcha.key' => 'YOUR RECAPTCHA KEY',
'Users.reCaptcha.secret' => 'YOUR RECAPTCHA SECRET',
'Users.reCaptcha.version' => '2', //defaults to version 2 (backward compatibility) but you can use version 3 which is recommended
'Users.reCaptcha.registration' => true, //enable on registration
'Users.reCaptcha.login' => true, //enable on login
```
Expand Down
2 changes: 2 additions & 0 deletions config/users.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
'key' => null,
// reCaptcha secret
'secret' => null,
// reCaptcha version. keep 2 for backward compatibility
'version' => 2,
// use reCaptcha in registration
'registration' => false,
// use reCaptcha in login, valid values are false, true
Expand Down
77 changes: 65 additions & 12 deletions src/View/Helper/UserHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
use Cake\Utility\Inflector;
use Cake\View\Helper;
use CakeDC\Users\Utility\UsersUrl;
use Exception;
use InvalidArgumentException;

/**
* User helper
Expand All @@ -42,7 +44,7 @@ class UserHelper extends Helper
* @param array $options options
* @return string
*/
public function socialLogin($name, $options = [])
public function socialLogin(string $name, array $options = []): string
{
if (empty($options['label'])) {
$options['label'] = __d('cake_d_c/users', 'Sign in with');
Expand Down Expand Up @@ -74,7 +76,7 @@ public function socialLogin($name, $options = [])
* @param array $providerOptions Provider link options.
* @return array Links to Social Login Urls
*/
public function socialLoginList(array $providerOptions = [])
public function socialLoginList(array $providerOptions = []): array
{
if (!Configure::read('Users.Social.login')) {
return [];
Expand Down Expand Up @@ -105,7 +107,7 @@ public function socialLoginList(array $providerOptions = [])
* @param array $options Array with option data.
* @return string
*/
public function logout($message = null, $options = [])
public function logout(?string $message = null, array $options = []): string
{
$url = UsersUrl::actionUrl('logout');
$title = empty($message) ? __d('cake_d_c/users', 'Logout') : $message;
Expand All @@ -118,7 +120,7 @@ public function logout($message = null, $options = [])
*
* @return string|null
*/
public function welcome()
public function welcome(): ?string
{
$identity = $this->getView()->getRequest()->getAttribute('identity');
if (!$identity) {
Expand All @@ -143,7 +145,7 @@ public function welcome()
*
* @return void
*/
public function addReCaptchaScript()
public function addReCaptchaScript(): void
{
$this->Html->script('https://www.google.com/recaptcha/api.js', [
'block' => 'script',
Expand All @@ -155,7 +157,7 @@ public function addReCaptchaScript()
*
* @return mixed
*/
public function addReCaptcha()
public function addReCaptcha(): mixed
{
if (!Configure::read('Users.reCaptcha.key')) {
return $this->Html->tag(
Expand All @@ -167,10 +169,29 @@ public function addReCaptcha()
);
}
$this->addReCaptchaScript();
try {
$this->Form->unlockField('g-recaptcha-response');
} catch (\Exception $e) {
$version = Configure::read('Users.reCaptcha.version', 2);
$method = "addReCaptchaV$version";
if (method_exists($this, $method)) {
try {
$this->Form->unlockField('g-recaptcha-response');
} catch (Exception $e) {
}

return $this->{$method}();
}
throw new InvalidArgumentException(
__d('cake_d_c/users', 'reCaptcha version is wrong. Please configure Users.reCaptcha.version as 2 or 3')
);
}

/**
* Add required element for reCaptcha v2
*
* @return string
*/
private function addReCaptchaV2(): string
{
deprecationWarning('14.2.0', 'reCaptcha version 3 will be used as default in version 15.0.0');

return $this->Html->tag('div', '', [
'class' => 'g-recaptcha',
Expand All @@ -181,6 +202,38 @@ public function addReCaptcha()
]);
}

/**
* Add required script for reCaptcha v3
*/
private function addReCaptchaV3(): void
{
$this->Html->script('CakeDC/Users.reCaptchaV3', [
'block' => 'script',
]);
}

/**
* Add required options for reCaptcha v3
*
* @param string $title
* @param array $options
* @return string
*/
public function button(string $title, array $options = []): string
{
$key = Configure::read('Users.reCaptcha.key');
if ($key && Configure::read('Users.reCaptcha.version', 2) === 3) {
$options = array_merge($options, [
'class' => 'g-recaptcha',
'data-sitekey' => $key,
'data-callback' => 'onSubmit',
'data-action' => 'submit',
]);
}

return $this->Form->button($title, $options);
}

/**
* Generate a link if the target url is authorized for the logged in user
*
Expand All @@ -190,7 +243,7 @@ public function addReCaptcha()
* @param array $options Array with option data.
* @return string
*/
public function link($title, $url = null, array $options = [])
public function link(string $title, array|string|null $url = null, array $options = []): string
{
trigger_error(
'UserHelper::link() deprecated since 3.2.1. Use AuthLinkHelper::link() instead',
Expand All @@ -208,7 +261,7 @@ public function link($title, $url = null, array $options = [])
* @param bool $isConnected User is connected with this provider
* @return string
*/
public function socialConnectLink($name, $provider, $isConnected = false)
public function socialConnectLink(string $name, array $provider, bool $isConnected = false): string
{
$optionClass = $provider['options']['class'] ?? null;
$linkClass = 'btn btn-social btn-' . strtolower($name) . ($optionClass ? ' ' . $optionClass : '');
Expand Down Expand Up @@ -236,7 +289,7 @@ public function socialConnectLink($name, $provider, $isConnected = false)
* @param array $socialAccounts All social accounts connected by a user.
* @return string
*/
public function socialConnectLinkList($socialAccounts = [])
public function socialConnectLinkList(array $socialAccounts = []): string
{
if (!Configure::read('Users.Social.login')) {
return '';
Expand Down
2 changes: 1 addition & 1 deletion templates/Users/login.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@
?>
</fieldset>
<?= implode(' ', $this->User->socialLoginList()); ?>
<?= $this->Form->button(__d('cake_d_c/users', 'Login')); ?>
<?= $this->User->button(__d('cake_d_c/users', 'Login')); ?>
<?= $this->Form->end() ?>
</div>
2 changes: 1 addition & 1 deletion templates/Users/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
}
?>
</fieldset>
<?= $this->Form->button(__d('cake_d_c/users', 'Submit')) ?>
<?= $this->User->button(__d('cake_d_c/users', 'Submit')) ?>
<?= $this->Form->end() ?>
</div>
54 changes: 54 additions & 0 deletions tests/TestCase/View/Helper/UserHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ class UserHelperTest extends TestCase
*/
private $AuthLink;

/**
* @var (\Cake\View\View&\PHPUnit\Framework\MockObject\MockObject)|\PHPUnit\Framework\MockObject\MockObject
*/
private $View;

/**
* @var ServerRequest
*/
private $request;

/**
* setUp method
*
Expand Down Expand Up @@ -237,6 +247,50 @@ public function testAddReCaptcha()
$this->assertEquals('<div class="g-recaptcha" data-sitekey="testKey" data-theme="light" data-size="normal" data-tabindex="3"></div>', $result);
}

/**
* Test add ReCaptcha V3
*
* @return void
*/
public function testAddReCaptchaV3()
{
$this->View->expects($this->exactly(2))
->method('append')
->willReturnMap([
['https://www.google.com/recaptcha/api.js', null],
['CakeDC/Users.reCaptchaV3', null],
]);
Configure::write('Users.reCaptcha.key', 'testKey');
Configure::write('Users.reCaptcha.version', 3);
Configure::write('Users.reCaptcha.theme', 'light');
Configure::write('Users.reCaptcha.size', 'normal');
Configure::write('Users.reCaptcha.tabindex', '3');
$this->User->Form->create();
$this->User->addReCaptcha();
}

public function testButton()
{
$title = 'test';
$options = ['test' => 'test'];
$this->assertEquals($this->User->Form->button($title, $options), $this->User->button($title, $options));
}

public function testButtonReCaptchaV3()
{
Configure::write('Users.reCaptcha.key', 'testKey');
Configure::write('Users.reCaptcha.version', 3);
$title = 'test';
$options = ['test' => 'test'];
$reCaptchaOptions = [
'class' => 'g-recaptcha',
'data-sitekey' => 'testKey',
'data-callback' => 'onSubmit',
'data-action' => 'submit',
];
$this->assertEquals($this->User->Form->button($title, array_merge($options, $reCaptchaOptions)), $this->User->button($title, $options));
}

/**
* Test add ReCaptcha field
*
Expand Down
3 changes: 3 additions & 0 deletions webroot/js/reCaptchaV3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function onSubmit(token) {
document.forms[0].submit()
}

0 comments on commit 4bb39ed

Please sign in to comment.