yandex_smartcaptcha-1.0.2/src/YandexSmartCaptcha.php
src/YandexSmartCaptcha.php
<?php
namespace Drupal\yandex_smartcaptcha;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
use Drupal\Core\Utility\Error;
use Exception\RuntimeException;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* The class implements integration with the Yandex SmartCaptcha service.
*/
class YandexSmartCaptcha {
/**
* The Yandex SmartCaptcha generated token.
*
* @var string
*/
private $token;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
public $configFactory;
/**
* The HTTP client.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $client;
/**
* The update settings.
*
* @var \Drupal\Core\Config\Config
*/
public $settings;
/**
* The attached_forms settings.
*
* @var \Drupal\Core\Config\Config
*/
public $attachedFormsSettings;
/**
* The file_mdm logger.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The Request Stack Service.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
private RequestStack $requestStack;
/**
* The messenger.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* Constructs a YandexSmartCaptchaService object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \GuzzleHttp\ClientInterface $client
* The HTTP client.
* @param \Psr\Log\LoggerInterface $logger
* The yandex_smartcaptcha logger.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* Request stack.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger.
*/
public function __construct(ConfigFactoryInterface $config_factory,
ClientInterface $client,
LoggerInterface $logger,
RequestStack $requestStack,
MessengerInterface $messenger) {
$this->settings = $config_factory->get('yandex_smartcaptcha.settings');
$this->attachedFormsSettings = $config_factory->get('yandex_smartcaptcha.attached_forms');
$this->client = $client;
$this->logger = $logger;
$this->requestStack = $requestStack;
$this->messenger = $messenger;
}
/**
* Verify Yandex SmartCaptcha token request.
*
* @param string $token
* The Yandex SmartCaptcha token value.
*
* @return bool
* Return TRUE if the token has been successfully verified, FALSE otherwise.
*/
public function verify(string $token) {
if (!$token) {
$this->logger->error('The Yandex SmartCaptcha token is empty');
return FALSE;
}
$this->token = $token;
$query = $this->getQuery($token);
$url = $this->settings->get('api_url') . '?' . $query;
try {
$response = $this->client->get($url, $this->getOptions());
}
catch (GuzzleException $e) {
throw new RuntimeException(Error::decodeException($e));
}
return $this->validateResponse($response);
}
/**
* SmartCaptcha response validation.
*
* @param \Psr\Http\Message\ResponseInterface $response
* The response.
*
* @return bool
* TRUE if response valid, FALSE otherwise.
*/
private function validateResponse(ResponseInterface $response) {
$status = $response->getStatusCode();
if ($status != 200) {
$this->logger->warning('HTTP error @errorcode occurred when trying to fetch @remote.', [
'@errorcode' => $status,
]);
return FALSE;
}
$data = Json::decode($response->getBody());
if ($data['status'] !== 'ok') {
$this->logger->warning('Request return error message @message', [
'@message' => $data['message'],
]);
return FALSE;
}
return TRUE;
}
/**
* Return verification query parameters.
*
* @param string|null $token
* The token value.
*
* @return string
* Url encoded string.
*/
private function getQuery(string $token = NULL) {
return http_build_query([
"secret" => $this->getSecretKey(),
"token" => $this->token,
"ip" => $this->getClientIp(),
]);
}
/**
* Return client request IP.
*/
private function getClientIp() {
return $this->requestStack->getCurrentRequest()->getClientIp();
}
/**
* Return SmartCaptcha site key.
*/
public function getSiteKey() {
$key = $this->isEnvVarsExists() ? getenv('YA_CAPTCHA_SITE_KEY') : $this->settings->get('site_key');
if (!$key) {
$this->logger->error('The site key is not provided');
$this->messenger->addError('The site key is not provided');
}
return $key;
}
/**
* Return SmartCaptcha secret key.
*/
public function getSecretKey() {
$key = $this->isEnvVarsExists() ? getenv('YA_CAPTCHA_SECRET_KEY') : $this->settings->get('secret_key');
if (!$key) {
$this->logger->error('The secret key is not provided');
$this->messenger->addError('The secret key is not provided');
}
return $key;
}
/**
* {@inheritdoc}
*/
private function getOptions() {
return [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/x-www-form-urlencoded',
],
];
}
/**
* Check if SmartCaptcha enabled or not.
*/
public function isEnabled() {
return $this->settings->get('enabled') || $this->isTestForm();
}
/**
* Check if SmartCaptcha logging enabled or not.
*/
public function isLog() {
return (bool) $this->settings->get('log');
}
/**
* Check if SmartCaptcha logging enabled or not.
*/
public function isTestForm() {
return $this->requestStack->getCurrentRequest()
->getRequestUri() == Url::fromRoute('yandex_smartcaptcha.test_form')
->toString();
}
/**
* Check if environment variables exists.
*/
public function isEnvVarsExists() {
return getenv('YA_CAPTCHA_SITE_KEY') && getenv('YA_CAPTCHA_SECRET_KEY');
}
/**
* Returns the Captcha attached forms array.
*/
public function getAttachedFormsIds() {
$form_ids = [];
foreach ($this->attachedFormsSettings->getRawData() as $k => $v) {
if ($k && isset($v['enabled'])) {
$form_ids[$k] = $v;
}
};
return $form_ids;
}
/**
* Return the webform page step where SmartCaptcha should be displayed.
*/
public function getCurrentWebformDisplayPage() {
/** @var \Drupal\webform\WebformRequestInterface $request_handler */
$request_handler = \Drupal::service('webform.request');
$webform = $request_handler->getCurrentWebform();
$webform_settings = $webform->getHandler('smartcaptcha_element')
->getSettings();
$pages = $this->getCurrentWebformPages();
$first_page = key($pages);
// Get the page where captcha should be displayed.
return $webform_settings['smartcaptcha']['display_page'] ?? $first_page;
}
/**
* Return SmartCaptcha form element.
*/
public function getCaptchaFormElement(array $settings = []) {
return [
'#type' => 'smartcaptcha_element',
'#smartcaptcha' => [
'invisible' => boolval($settings['invisible']),
'test' => boolval($settings['test']),
'button_hide_mode' => $settings['button_hide_mode'] ?? 'hide',
],
];
}
/**
* Return webform pages array.
*/
public function getCurrentWebformPages() {
/** @var \Drupal\webform\WebformRequestInterface $request_handler */
$request_handler = \Drupal::service('webform.request');
$webform = $request_handler->getCurrentWebform();
$options = $webform->getPages();
array_walk($options, function ($v, $k) use (&$options) {
$options[$k] = $v['#title'];
});
$options = array_diff_key($options, ['webform_confirmation' => '']);
return $options;
}
}
