Skip to content

Commit

Permalink
Merge pull request #33 from andysh-uk/master
Browse files Browse the repository at this point in the history
Implemented CAPTCHA options
  • Loading branch information
I-Valchev authored Nov 27, 2020
2 parents 6107d6c + 81e4f9d commit cb4e5f5
Show file tree
Hide file tree
Showing 16 changed files with 659 additions and 10 deletions.
75 changes: 75 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,78 @@ configuration:
ip: ~ # do not save the ip
attachments: ~ # do not save attached files
```

### Add CAPTCHA challenges to protect against bots

Bolt forms support the following CAPTCHA platforms:

* [Google reCAPTCHA](https://www.google.com/recaptcha/about/) (v3, v2 checkbox, v2 invisible)
* [hCaptcha](https://www.hcaptcha.com/)

You will need to obtain a site key and secret key from either of the above platforms.

**Please note it is not possible to use both. hCaptcha has been designed to easily replace reCAPTCHA, so it takes over.**

Uncomment either the `hcaptcha` or `recaptcha` nodes in your config, and populate the public_key (site key) and
private_key (secret key) settings. Set the `enabled` node to true.

Please note: `theme` can either be `light` or `dark` - it only applies to hCaptcha and reCAPTCHA v2 checkbox.)

hCaptcha:

```yaml
hcaptcha:
enabled: true
public_key: "..."
private_key: "..."
theme: dark
```

reCAPTCHA:

```yaml
recaptcha:
enabled: true
public_key: '...'
private_key: '...'
theme: light
```

Finally, insert a captcha field in your form where you would like the CAPTCHA challenge to appear. If an invisible
CAPTCHA is being used (reCAPTCHA v3 or v2 invisible), this is where any validation errors will be rendered.

This field must be before the submit button for reCAPTCHA v3 and v2 invisible. The field name can
be anything as long as it is unique within your form.

```yaml
contact:
fields:
# hCaptcha:
my_hcaptcha_field:
type: captcha
options:
captcha_type: hcaptcha
# reCAPTCHA v3:
my_recaptcha_v3_field:
type: captcha
options:
captcha_type: recaptcha_v3
# reCAPTCHA v2 checkbox:
my_recaptcha_v2_checkbox_field:
type: captcha
options:
captcha_type: recaptcha_v2
# Use "label: false" to hide the label
label: Please complete this CAPTCHA
# reCAPTCHA v2 invisible:
my_recaptcha_v2_invisible_field:
type: captcha
options:
captcha_type: recaptcha_v2
captcha_invisible: true
# submit button must come after the CAPTCHA
```
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
],
"require": {
"php": ">=7.2.2",
"twig/twig": "^2.12 | ^3.0"
"twig/twig": "^2.12 | ^3.0",
"ext/json": "*"
},
"require-dev": {
"bolt/core": "^4.0.0",
Expand Down
35 changes: 32 additions & 3 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,21 @@ layout:
#
#recaptcha:
# enabled: false
# label: "Please enter the reCaptcha text to prove you're a human"
# public_key: ''
# private_key: ''
# error_message: "The CAPTCHA wasn't entered correctly. Please try again."
# theme: clean
# theme: light|dark

## hCaptcha set up
#
# NOTE: You can get your keys from https://dashboard.hcaptcha.com/sites
# * `public_key` key will be labeled "Site key" within the Site configuration
# * `private_key` key will be labeled "Secret key" within the Settings tab
#
#hcaptcha:
# enabled: false
# public_key: ''
# private_key: ''
# theme: light|dark

## File Uploads
#
Expand Down Expand Up @@ -333,6 +343,25 @@ contact:
# options:
# required: false
# label: Picture of your pet that you want us to add to our site
# hcaptcha:
# type: captcha
# options:
# captcha_type: hcaptcha
# recaptcha_v3:
# type: captcha
# options:
# captcha_type: recaptcha_v3
# recaptcha_v2:
# type: captcha
# options:
# captcha_type: recaptcha_v2
# # To not show a label at all, use "label: false"
# label: Please complete this CAPTCHA
# recaptcha_v2_invisible:
# type: captcha
# options:
# captcha_type: recaptcha_v2
# captcha_invisible: true
# submit:
# type: submit
# options:
Expand Down
7 changes: 7 additions & 0 deletions src/CaptchaException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Bolt\BoltForms;

class CaptchaException extends \Exception
{
}
41 changes: 40 additions & 1 deletion src/Factory/FieldOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

namespace Bolt\BoltForms\Factory;

use Bolt\BoltForms\Validator\Constraints\Hcaptcha;
use Bolt\BoltForms\Validator\Constraints\Recaptcha;
use Tightenco\Collect\Support\Collection;

class FieldOptions
{
public static function get(string $formName, array $field): array
public static function get(string $formName, array $field, Collection $config): array
{
if (! array_key_exists('options', $field)) {
return [];
Expand All @@ -18,6 +22,41 @@ public static function get(string $formName, array $field): array

if ($field['type'] === 'submit' || $field['type'] === 'button') {
unset($options['constraints']);
} elseif ($field['type'] === 'captcha') {
if ($config->has('hcaptcha')) {
$options['hcaptcha_public_key'] = $config['hcaptcha']['public_key'];

if (isset($config['hcaptcha']['theme'])) {
$options['hcaptcha_theme'] = $config['hcaptcha']['theme'];
}
}

if ($config->has('recaptcha')) {
$options['recaptcha_public_key'] = $config['recaptcha']['public_key'];

if (isset($config['recaptcha']['theme'])) {
$options['recaptcha_theme'] = $config['recaptcha']['theme'];
}
}

unset($options['constraints']);

if (isset($options['captcha_type'])) {
switch ($options['captcha_type']) {
case 'hcaptcha':
$options['constraints'] = [
new Hcaptcha($config['hcaptcha']['public_key'], $config['hcaptcha']['private_key'])
];
break;

case 'recaptcha_v3':
case 'recaptcha_v2':
$options['constraints'] = [
new Recaptcha($config['recaptcha']['public_key'], $config['recaptcha']['private_key'])
];
break;
}
}
}

return $options;
Expand Down
4 changes: 4 additions & 0 deletions src/Factory/FieldType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Bolt\BoltForms\Factory;

use Bolt\BoltForms\Form\CaptchaType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
Expand Down Expand Up @@ -55,6 +56,9 @@ public static function get($field): string
case 'button':
$type = ButtonType::class;
break;
case 'captcha':
$type = CaptchaType::class;
break;
case 'text':
default:
$type = TextType::class;
Expand Down
39 changes: 39 additions & 0 deletions src/Form/CaptchaType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Bolt\BoltForms\Form;

use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CaptchaType extends HiddenType
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['captcha_type'] = $options['captcha_type'];
$view->vars['captcha_invisible'] = $options['captcha_invisible'];
$view->vars['hcaptcha_public_key'] = $options['hcaptcha_public_key'];
$view->vars['hcaptcha_theme'] = $options['hcaptcha_theme'];
$view->vars['recaptcha_public_key'] = $options['recaptcha_public_key'];
$view->vars['recaptcha_theme'] = $options['recaptcha_theme'];
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'compound' => false,
'captcha_type' => '',
'captcha_invisible' => false,
'hcaptcha_theme' => 'light',
'recaptcha_theme' => 'light',
'hcaptcha_public_key' => '',
'recaptcha_public_key' => '',
]);
}

public function getBlockPrefix()
{
return 'captcha';
}
}
Loading

0 comments on commit cb4e5f5

Please sign in to comment.