Skip to content
This repository was archived by the owner on Jan 11, 2021. It is now read-only.
Open
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
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ cache:
matrix:
fast_finish: true
include:
- php: 7.1
env: SYMFONY=^3
- php: 7.1
env: SYMFONY=^4
- php: 7.2
env: SYMFONY=^3
- php: 7.2
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pr_recaptcha:
public_key: 'public key'
secret_key: 'secret key'
enabled: false # optional / default value: true - you can disable it for local or test env
version: 2 # optional / default value 3 - version of reCAPTCHA
score_threshhold: 'score' # optional / default value: 0.5
hide_badge: true # optional / default value: false *
host: 'www.google.com' # optional / default value: www.google.com **
Expand All @@ -36,6 +37,17 @@ Add field with type `RecaptchaType` to your form, example:

`->add('captcha', RecaptchaType::class)`

##### Custom action name
If you want to use custom action name to analyze data from each form separately, set action name (default name: `form`)
```
->add('captcha', RecaptchaType::class, [
'attr' => [
'options' => [
'action_name' => 'My form'
]
]
])
```

#### TODO
1. Support for version v2
2. Waiting for suggestions :)
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
}
],
"require": {
"php": "^7.0",
"php": "^7.1",
"google/recaptcha": "^1.1",
"symfony/form": "^2.8 || ^3.0 || ^4.0",
"twig/twig": "^2.0",
"symfony/framework-bundle": "^2.8 || ^3.0 || ^4.0",
"symfony/validator": "^2.8 || ^3.0 || ^4.0"
},
"require-dev": {
"phpunit/phpunit": "^7 || ^8"
},
"autoload": {
"psr-4": {
"PR\\Bundle\\RecaptchaBundle\\": "src"
Expand All @@ -31,8 +34,5 @@
"symfony": {
"allow-contrib": "true"
}
},
"require-dev": {
"phpunit/phpunit": "^8"
}
}
3 changes: 1 addition & 2 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
colors="true">

<testsuites>
<testsuite name="EWZRecaptchaBundle">
<testsuite name="PRRecaptchaBundle">
<directory>tests</directory>
</testsuite>
</testsuites>
Expand All @@ -16,5 +16,4 @@
<directory suffix=".php">src</directory>
</whitelist>
</filter>

</phpunit>
4 changes: 4 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public function getConfigTreeBuilder()
->booleanNode('enabled')
->defaultValue(true)
->end()
->enumNode('version')
->values([2, 3])
->defaultValue(3)
->end()
->floatNode('score_threshhold')
->defaultValue(0.5)
->end()
Expand Down
16 changes: 13 additions & 3 deletions src/Form/Type/RecaptchaType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

final class RecaptchaType extends AbstractType
{
/** @var int */
private $version;

/** @var string */
private $publicKey;

Expand All @@ -20,15 +23,21 @@ final class RecaptchaType extends AbstractType

/** @var string */
private $host;

/**
* RecaptchaType constructor.
*
* @param int $version
* @param string $publicKey
* @param bool $hideBadge
* @param string $host
*/
public function __construct(string $publicKey, bool $hideBadge, string $host)
{
public function __construct(
int $version,
string $publicKey,
bool $hideBadge,
string $host
) {
$this->version = $version;
$this->publicKey = $publicKey;
$this->hideBadge = $hideBadge;
$this->host = $host;
Expand All @@ -40,6 +49,7 @@ public function __construct(string $publicKey, bool $hideBadge, string $host)
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars = array_replace($view->vars, [
'pr_recaptcha_version' => $this->version,
'pr_recaptcha_public_key' => $this->publicKey,
'pr_recaptcha_hide_badge' => $this->hideBadge,
'pr_recaptcha_host' => $this->host
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ services:
PR\Bundle\RecaptchaBundle\Form\Type\RecaptchaType:
public: true
arguments:
- '%pr_recaptcha.version%'
- '%pr_recaptcha.public_key%'
- '%pr_recaptcha.hide_badge%'
- '%pr_recaptcha.host%'
Expand All @@ -12,6 +13,7 @@ services:
public: true
arguments:
- '%pr_recaptcha.enabled%'
- '%pr_recaptcha.version%'
- '%pr_recaptcha.secret_key%'
- '%pr_recaptcha.score_threshhold%'
- '@request_stack'
Expand Down
38 changes: 29 additions & 9 deletions src/Resources/views/Form/recaptcha.html.twig
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
{% block pr_recaptcha_widget %}
<script src="https://{{ form.vars.pr_recaptcha_host }}/recaptcha/api.js?render={{ form.vars.pr_recaptcha_public_key }}"></script>
{% set options = attr.options %}

{% if form.vars.pr_recaptcha_hide_badge %}
{% if form.vars.pr_recaptcha_version == 2 %}
<script src="https://{{ form.vars.pr_recaptcha_host }}/recaptcha/api.js?onload=onloadCallback{{ id }}"
{% if options.defer is defined and options.defer %} defer{% endif %}
{% if options.async is defined and options.async %} async{% endif %}
></script>
{% else %}
<script src="https://{{ form.vars.pr_recaptcha_host }}/recaptcha/api.js?render={{ form.vars.pr_recaptcha_public_key }}"></script>
{% endif %}

{% if form.vars.pr_recaptcha_hide_badge and form.vars.pr_recaptcha_version == 3 %}
<link rel="stylesheet" href="{{ asset('/bundles/prrecaptcha/css/recaptcha.css') }}">
{% endif %}

<script>
grecaptcha.ready(function () {
grecaptcha.execute('{{ form.vars.pr_recaptcha_public_key }}', { action: 'form' }).then(function (token) {
var recaptchaResponse = document.getElementById('{{ id }}');
recaptchaResponse.value = token;
{% if form.vars.pr_recaptcha_version == 2 %}
<div id="{{ id }}-recaptcha"></div>
<script>
var onloadCallback{{ id }} = function() {
grecaptcha.render('{{ id }}-recaptcha', {
'sitekey': '{{ form.vars.pr_recaptcha_public_key }}'
});
};
</script>
{% else %}
<script>
grecaptcha.ready(function () {
grecaptcha.execute('{{ form.vars.pr_recaptcha_public_key }}', { action: '{{ options.action_name ?? 'form' }}' }).then(function (token) {
var recaptchaResponse = document.getElementById('{{ id }}');
recaptchaResponse.value = token;
});
});
});
</script>
</script>
{% endif %}

{{ form_label(form) }}
{{ form_widget(form) }}
Expand Down
20 changes: 15 additions & 5 deletions src/Validator/Constraints/ContainsRecaptchaValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ final class ContainsRecaptchaValidator extends ConstraintValidator
/** @var bool */
private $enabled;

/** @var int */
private $version;

/** @var string */
private $secretKey;

Expand All @@ -30,19 +33,22 @@ final class ContainsRecaptchaValidator extends ConstraintValidator
/**
* ContainsRecaptchaValidator constructor.
* @param bool $enabled
* @param int $version
* @param string $secretKey
* @param float $scoreThreshhold
* @param RequestStack $requestStack
* @param LoggerInterface $logger
*/
public function __construct(
bool $enabled,
int $version,
string $secretKey,
float $scoreThreshhold,
RequestStack $requestStack,
LoggerInterface $logger
) {
$this->enabled = $enabled;
$this->version = $version;
$this->secretKey = $secretKey;
$this->scoreThreshhold = $scoreThreshhold;
$this->requestStack = $requestStack;
Expand Down Expand Up @@ -86,13 +92,17 @@ private function isTokenValid(string $token): bool
{
try {
$remoteIp = $this->requestStack->getCurrentRequest()->getClientIp();

$recaptcha = new ReCaptcha($this->secretKey);

$response = $recaptcha
->setExpectedAction('form')
->setScoreThreshold($this->scoreThreshhold)
->verify($token, $remoteIp);
if ($this->version === 3) {
$action = $this->context->getObject()->getConfig()->getAttributes()['data_collector/passed_options']['attr']['action_name'] ?? 'form';

$recaptcha
->setExpectedAction($action)
->setScoreThreshold($this->scoreThreshhold);
}

$response = $recaptcha->verify($token, $remoteIp);

return $response->isSuccess();
} catch (\Exception $exception) {
Expand Down
8 changes: 4 additions & 4 deletions tests/Form/Type/RecaptchaTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class EWZRecaptchaTypeTest extends TestCase
final class RecaptchaTypeTest extends TestCase
{
/** @var RecaptchaType */
private $formType;

protected function setUp(): void
{
$this->formType = new RecaptchaType('publicKey', true, 'www.google.com');
$this->formType = new RecaptchaType(3,'publicKey', true, 'www.google.com');
}

/**
Expand Down Expand Up @@ -47,9 +47,9 @@ public function buildView(): void

$this->formType->buildView($view, $form, []);

$this->assertStringContainsString('publicKey', $view->vars['pr_recaptcha_public_key']);
$this->assertEquals('publicKey', $view->vars['pr_recaptcha_public_key']);
$this->assertTrue($view->vars['pr_recaptcha_hide_badge']);
$this->assertStringContainsString('www.google.com', $view->vars['pr_recaptcha_host']);
$this->assertEquals('www.google.com', $view->vars['pr_recaptcha_host']);
}

/**
Expand Down