class SimpleReCaptchaFormManager in Simple Google reCAPTCHA 8
Provides helper service used to attach reCaptcha to forms.
Hierarchy
- class \Drupal\simple_recaptcha\SimpleReCaptchaFormManager uses DependencySerializationTrait, StringTranslationTrait
Expanded class hierarchy of SimpleReCaptchaFormManager
3 files declare their use of SimpleReCaptchaFormManager
- SimpleReCaptchaFormManagerTest.php in tests/
src/ Unit/ SimpleReCaptchaFormManagerTest.php - SimpleRecaptchaWebformHandler.php in modules/
simple_recaptcha_webform/ src/ Plugin/ WebformHandler/ SimpleRecaptchaWebformHandler.php - simple_recaptcha.module in ./
simple_recaptcha.module - Provides common hooks and functions for simple_google_recaptcha module.
1 string reference to 'SimpleReCaptchaFormManager'
1 service uses SimpleReCaptchaFormManager
File
- src/
SimpleReCaptchaFormManager.php, line 19
Namespace
Drupal\simple_recaptchaView source
class SimpleReCaptchaFormManager {
use DependencySerializationTrait;
use StringTranslationTrait;
/**
* The configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The GuzzleHttp client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $client;
/**
* Logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactory
*/
protected $logger;
/**
* Module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Session service.
*
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
protected $session;
/**
* Constructs a SimpleReCaptchaFormManager object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory.
* @param \GuzzleHttp\ClientInterface $client
* Http client to connect with reCAPTCHA verify service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger
* The logger factory.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* Module handler service.
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* The session service.
*/
public function __construct(ConfigFactoryInterface $config_factory, ClientInterface $client, LoggerChannelFactoryInterface $logger, ModuleHandlerInterface $module_handler, SessionInterface $session) {
$this->configFactory = $config_factory;
$this->client = $client;
$this->logger = $logger;
$this->moduleHandler = $module_handler;
$this->session = $session;
}
/**
* Add reCaptcha v2 container and libraries to the form.
*
* @param array $form
* Renderable array of form which will be secured by reCaptcha checkbox.
* @param string $form_id
* Form ID of form which will be secured.
*/
public function addReCaptchaCheckbox(array &$form, $form_id) {
// Allow modules to perform extra access checks and bypass validation.
$bypass = FALSE;
$this->moduleHandler
->alter('simple_recaptcha_bypass', $form, $bypass);
if ($bypass) {
return;
}
// Check if site keys are configured, if at least one of keys isn't provided
// protection won't work, so we can't modify and block this form.
$config = $this->configFactory
->get('simple_recaptcha.config');
$site_key = $config
->get('site_key');
$secret_key = $config
->get('secret_key');
if (!$site_key || !$secret_key) {
return;
}
// Add HTML data attributes and Wrapper for reCAPTCHA widget.
$form['#attributes']['data-recaptcha-id'] = $form_id;
$form['actions']['captcha'] = [
'#type' => 'container',
'#weight' => -1,
'#attributes' => [
'id' => $form_id . '-captcha',
'class' => [
'recaptcha',
'recaptcha-wrapper',
],
],
];
// Attach helper libraries.
$form['#attached']['drupalSettings']['simple_recaptcha']['sitekey'] = $site_key;
$form['#attached']['drupalSettings']['simple_recaptcha']['form_ids'][$form_id] = $form_id;
$form['#attached']['library'][] = 'simple_recaptcha/simple_recaptcha';
$form['simple_recaptcha_token'] = [
'#type' => 'hidden',
];
$form['simple_recaptcha_type'] = [
'#type' => 'hidden',
'#value' => 'v2',
];
$form['#validate'][] = [
$this,
'validateCaptchaToken',
];
// Marge config cache into existing form cache metadata.
$form_cache = CacheableMetadata::createFromRenderArray($form);
$config_cache = CacheableMetadata::createFromObject($config);
$form_cache
->merge($config_cache)
->applyTo($form);
$this
->addSubmitHandler($form);
}
/**
* Add reCaptcha v3 container and libraries to the form.
*
* @param array $form
* Renderable array of form which will be secured by reCaptcha checkbox.
* @param string $form_id
* Form ID of form which will be secured.
* @param array $configuration
* Configuration for invisible recaptcha.
*/
public function addReCaptchaInvisible(array &$form, $form_id, array $configuration) {
// Allow modules to perform extra access checks and bypass validation.
$bypass = FALSE;
$this->moduleHandler
->alter('simple_recaptcha_bypass', $form, $bypass);
if ($bypass) {
return;
}
// Check if site keys are configured, if at least one of keys isn't provided
// protection won't work, so we can't modify and block this form.
$config = $this->configFactory
->get('simple_recaptcha.config');
$site_key = $config
->get('site_key_v3');
$secret_key = $config
->get('secret_key_v3');
if (!$site_key || !$secret_key) {
return;
}
// Add HTML data attributes and Wrapper for reCAPTCHA widget.
$form['#attributes']['data-recaptcha-id'] = $form_id;
$form['actions']['captcha'] = [
'#type' => 'container',
'#weight' => -1,
'#attributes' => [
'id' => $form_id . '-captcha',
'class' => [
'recaptcha-v3',
'recaptcha-v3-wrapper',
],
],
];
$form['#attached']['drupalSettings']['simple_recaptcha_v3']['sitekey'] = $site_key;
$form['#attached']['drupalSettings']['simple_recaptcha_v3']['forms'][$form_id] = [
'form_id' => $form_id,
'score' => $configuration['v3_score'],
'error_message' => isset($configuration['v3_error_message']) ? $configuration['v3_error_message'] : NULL,
'action' => $configuration['recaptcha_action'],
];
$form['#attached']['library'][] = 'simple_recaptcha/simple_recaptcha_v3';
$form['simple_recaptcha_token'] = [
'#type' => 'hidden',
];
$form['simple_recaptcha_type'] = [
'#type' => 'hidden',
'#value' => 'v3',
];
$form['simple_recaptcha_score'] = [
'#type' => 'hidden',
'#value' => $configuration['v3_score'],
];
$form['simple_recaptcha_message'] = [
'#type' => 'hidden',
];
$form['#validate'][] = [
$this,
'validateCaptchaToken',
];
// Marge config cache into existing form cache metadata.
$form_cache = CacheableMetadata::createFromRenderArray($form);
$config_cache = CacheableMetadata::createFromObject($config);
$form_cache
->merge($config_cache)
->applyTo($form);
$this
->addSubmitHandler($form);
}
/**
* Validates form with reCAPTCHA protection enabled.
*/
public function validateCaptchaToken(&$form, FormStateInterface &$form_state) {
// Check if valid token is already present in the session.
$session_key = 'simple_recaptcha';
$stored_token = $this->session
->has($session_key) ? $this->session
->get($session_key) : '';
$token = $form_state
->getValue('simple_recaptcha_token');
if (strlen($token) > 0 && strlen($stored_token) > 0 && $stored_token == $token) {
return;
}
$message = $form_state
->getValue('simple_recaptcha_message');
if (!$message) {
$message = $this
->t('There was an error during validation of your form submission, please try to reload the page and submit form again.');
}
$type = $form_state
->getValue('simple_recaptcha_type');
$config = $this->configFactory
->get('simple_recaptcha.config');
$config_secret_key = $type == 'v2' ? $config
->get('secret_key') : $config
->get('secret_key_v3');
// Verify reCAPTCHA token.
$params = [
'secret' => $config_secret_key,
'response' => $token,
];
$url = 'https://www.google.com/recaptcha/api/siteverify';
if ($config
->get('recaptcha_use_globally')) {
$url = 'https://www.recaptcha.net/recaptcha/api/siteverify';
}
$request = $this->client
->post($url, [
'form_params' => $params,
]);
$api_response = Json::decode($request
->getBody()
->getContents());
if (!$api_response['success']) {
$this->logger
->get('simple_recaptcha')
->notice($this
->t('reCAPTCHA validation failed, error codes: @errors', [
'@errors' => implode(',', $api_response['error-codes']),
]));
$form_state
->setError($form, $message);
}
// Verify score for reCAPTCHA v3.
if ($type == 'v3' && isset($api_response['score'])) {
$desired_score = $form_state
->getValue('simple_recaptcha_score');
$api_score = $api_response['score'] * 100;
if ($api_score < $desired_score) {
$this->logger
->get('simple_recaptcha')
->notice($this
->t('reCAPTCHA validation failed, reCAPTCHA score too low: @score (desired score was @desired_score)', [
'@score' => $api_score,
'@desired_score' => $desired_score,
]));
$form_state
->setError($form, $message);
}
}
// If API response is valid, store current token in the user's session
// so we won't have to validate this form again.
if ($api_response['success']) {
$this->session
->set($session_key, $token);
}
}
/**
* Submit callback for form to clear no-longer needed session data.
*
* The session will automatically terminate if this was the only thin in it.
*/
public function clearSessionData(&$form, FormStateInterface &$form_state) {
$session_key = 'simple_recaptcha';
if ($this->session
->has($session_key)) {
$this->session
->remove($session_key);
}
}
/**
* Check whether the needle is in the haystack.
*
* @param string $needle
* The needle which is checked.
* @param string[] $haystack
* A list of identifiers to determine whether $needle is in it.
*
* @return bool
* True if the needle is in the haystack.
*/
public static function formIdInList($needle, array $haystack) {
// Prepare the haystack for regex matching by quoting all regex symbols and
// replacing back the original '*' with '.*' to allow it to catch all.
$haystack = array_map(function ($line) {
return str_replace('\\*', '.*', preg_quote($line, '/'));
}, $haystack);
foreach ($haystack as $line) {
if (preg_match('/^' . $line . '$/', $needle)) {
return TRUE;
}
}
return FALSE;
}
/**
* Add our custom submit handler to the form.
*
* @param array $form
* The form.
*/
protected function addSubmitHandler(&$form) {
// We need to register a custom submit handler to clear out session data
// we no longer need, but we cannot just add it to the base form array
// #submit property, since action-specific handlers override this. First we
// check if any of those exist and add it to them instead.
$specificActionHandlersUsed = FALSE;
if (isset($form['actions'])) {
foreach (array_keys($form['actions']) as $action) {
if (isset($form['actions'][$action]['#submit'])) {
$form['actions'][$action]['#submit'][] = [
$this,
'clearSessionData',
];
$specificActionHandlersUsed = TRUE;
}
}
}
if (!$specificActionHandlersUsed) {
$form['#submit'][] = [
$this,
'clearSessionData',
];
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
SimpleReCaptchaFormManager:: |
protected | property | The GuzzleHttp client. | |
SimpleReCaptchaFormManager:: |
protected | property | The configuration factory. | |
SimpleReCaptchaFormManager:: |
protected | property | Logger factory. | |
SimpleReCaptchaFormManager:: |
protected | property | Module handler service. | |
SimpleReCaptchaFormManager:: |
protected | property | Session service. | |
SimpleReCaptchaFormManager:: |
public | function | Add reCaptcha v2 container and libraries to the form. | |
SimpleReCaptchaFormManager:: |
public | function | Add reCaptcha v3 container and libraries to the form. | |
SimpleReCaptchaFormManager:: |
protected | function | Add our custom submit handler to the form. | |
SimpleReCaptchaFormManager:: |
public | function | Submit callback for form to clear no-longer needed session data. | |
SimpleReCaptchaFormManager:: |
public static | function | Check whether the needle is in the haystack. | |
SimpleReCaptchaFormManager:: |
public | function | Validates form with reCAPTCHA protection enabled. | |
SimpleReCaptchaFormManager:: |
public | function | Constructs a SimpleReCaptchaFormManager object. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |