Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reCaptcha v3 #1085

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
ajibarra marked this conversation as resolved.
Show resolved Hide resolved
{
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) {
ajibarra marked this conversation as resolved.
Show resolved Hide resolved
}

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')) ?>
ajibarra marked this conversation as resolved.
Show resolved Hide resolved
<?= $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()
}
Loading