View source
<?php
namespace Drupal\simple_recaptcha;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use GuzzleHttp\ClientInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class SimpleReCaptchaFormManager {
use DependencySerializationTrait;
use StringTranslationTrait;
protected $configFactory;
protected $client;
protected $logger;
protected $moduleHandler;
protected $session;
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;
}
public function addReCaptchaCheckbox(array &$form, $form_id) {
$bypass = FALSE;
$this->moduleHandler
->alter('simple_recaptcha_bypass', $form, $bypass);
if ($bypass) {
return;
}
$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;
}
$form['#attributes']['data-recaptcha-id'] = $form_id;
$form['actions']['captcha'] = [
'#type' => 'container',
'#weight' => -1,
'#attributes' => [
'id' => $form_id . '-captcha',
'class' => [
'recaptcha',
'recaptcha-wrapper',
],
],
];
$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',
];
$form_cache = CacheableMetadata::createFromRenderArray($form);
$config_cache = CacheableMetadata::createFromObject($config);
$form_cache
->merge($config_cache)
->applyTo($form);
$this
->addSubmitHandler($form);
}
public function addReCaptchaInvisible(array &$form, $form_id, array $configuration) {
$bypass = FALSE;
$this->moduleHandler
->alter('simple_recaptcha_bypass', $form, $bypass);
if ($bypass) {
return;
}
$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;
}
$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',
];
$form_cache = CacheableMetadata::createFromRenderArray($form);
$config_cache = CacheableMetadata::createFromObject($config);
$form_cache
->merge($config_cache)
->applyTo($form);
$this
->addSubmitHandler($form);
}
public function validateCaptchaToken(&$form, FormStateInterface &$form_state) {
$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');
$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);
}
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['success']) {
$this->session
->set($session_key, $token);
}
}
public function clearSessionData(&$form, FormStateInterface &$form_state) {
$session_key = 'simple_recaptcha';
if ($this->session
->has($session_key)) {
$this->session
->remove($session_key);
}
}
public static function formIdInList($needle, array $haystack) {
$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;
}
protected function addSubmitHandler(&$form) {
$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',
];
}
}
}