<?php
|
|
namespace JVBase\integrations\services;
|
use JVBase\integrations\Admin;
|
use JVBase\integrations\Instance;
|
use JVBase\integrations\Integrations;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
class Cloudflare extends Integrations
|
{
|
use Admin, Instance;
|
private string $theme = 'light';
|
private string $size = 'normal';
|
|
protected function __construct()
|
{
|
$this->service_name = 'cloudflare';
|
$this->title = 'Cloudflare';
|
$this->icon = 'cloud';
|
|
$this->defineFields();
|
$this->defineInstructions();
|
parent::__construct();
|
}
|
|
protected function defineFields():void
|
{
|
$site_key = $this->addField('site_key', 'Site Key', 'text', 'manage_options');
|
$site_key->setRequired();
|
$site_key->setHint('Public key for Turnstile Widget');
|
$secret_key = $this->addField('secret_key', 'Secret Key', 'text', 'manage_options');
|
$secret_key->setRequired();
|
$secret_key->setSubType('password');
|
$secret_key->setHint('Secret key for server-side verification of Turnstile Widget');
|
}
|
|
protected function defineInstructions():void
|
{
|
$this->instructions = [
|
'Go to <a href="https://dash.cloudflare.com/?to=/:account/turnstile" target="_blank">Cloudflare Dashboard</a>',
|
'Create a new Turnstile widget',
|
'Add your domain to the widget settings',
|
'Copy the Site Key and Secret Key'
|
];
|
}
|
protected function initialize(): void
|
{
|
// TODO: Implement initialize() method.
|
}
|
|
public function getServiceDescription():string
|
{
|
return "Add Turnstile captcha protection to your forms and login pages to prevent spam and bot submissions.";
|
}
|
|
public function performConnectionTest(): bool
|
{
|
$credentials = $this->loadCredentials();
|
if (!array_key_exists('site_key', $credentials) || !array_key_exists('secret_key', $credentials)) {
|
return false;
|
}
|
|
// Test with a dummy token to verify API connectivity
|
$response = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
'body' => [
|
'secret' => $credentials['secret_key'],
|
'response' => 'test-token-connection-check'
|
],
|
'timeout' => 10
|
]);
|
|
// We expect this to fail with success:false, but not with a connection error
|
if (is_wp_error($response)) {
|
return false;
|
}
|
|
$result = json_decode(wp_remote_retrieve_body($response), true);
|
// If we get a response with success field (even if false), connection works
|
return array_key_exists('success', $result);
|
}
|
|
/**
|
* Enqueue Turnstile script
|
*/
|
public function enqueueTurnstileScripts(): void
|
{
|
wp_enqueue_script(
|
'cloudflare-turnstile',
|
'https://challenges.cloudflare.com/turnstile/v0/api.js',
|
[],
|
null,
|
true
|
);
|
wp_script_add_data('cloudflare-turnstile', 'async', true);
|
wp_script_add_data('cloudflare-turnstile', 'defer', true);
|
}
|
|
/**
|
* Render Turnstile widget
|
* Can be called directly: jvbConnect('cloudflare')->renderTurnstile()
|
* Or Cloudflare::getInstance()->renderTurnstile()
|
*/
|
public function renderTurnstile(array|string $options = []): void
|
{
|
if (!$this->isSetUp()) {
|
return;
|
}
|
|
if (!is_array($options)) {
|
$options = [];
|
}
|
|
$defaults = [
|
'theme' => $this->theme,
|
'size' => $this->size,
|
'wrapper_class' => 'cf-turnstile-wrapper',
|
'wrapper_style' => 'margin: 1em 0;'
|
];
|
|
$cred = $this->loadCredentials();
|
$options = wp_parse_args($options, $defaults);
|
|
echo sprintf(
|
'<div class="%s" style="%s">
|
<div class="cf-turnstile" data-sitekey="%s" data-theme="%s" data-size="%s"></div>
|
</div>',
|
esc_attr($options['wrapper_class']),
|
esc_attr($options['wrapper_style']),
|
$cred['site_key'],
|
$options['theme'],
|
$options['size']
|
);
|
}
|
|
/**
|
* Verify the Turnstile response
|
* Can be called directly via JVB()->connect('cloudflare')->verifyTurnstile($token)
|
* or Cloudflare::getInstance()->verifyTurnstile($token)
|
* @param string|null $token
|
* @param string $remote_ip
|
* @return bool
|
*/
|
public function verifyTurnstile(?string $token = null, string $remote_ip = ''):bool
|
{
|
if (!$this->isSetUp()) {
|
return true;
|
}
|
|
// If no token provided, try to get it from $_POST
|
if (!$token && isset($_POST['cf-turnstile-response'])) {
|
$token = sanitize_text_field($_POST['cf-turnstile-response']);
|
}
|
|
if (empty($token)) {
|
return false;
|
}
|
|
$cred = $this->loadCredentials();
|
$data = [
|
'secret' => $cred['secret_key'],
|
'response' => $token,
|
];
|
|
if (empty($remote_ip) && isset($_SERVER['REMOTE_ADDR'])) {
|
$remote_ip = sanitize_text_field($_SERVER['REMOTE_ADDR']);
|
}
|
|
if (!empty($remote_ip)) {
|
$data['remoteip'] = $remote_ip;
|
}
|
$response = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
'body' => $data,
|
'timeout' => 10
|
]);
|
|
if (is_wp_error($response)) {
|
$this->logError('verifyTurnstile', 'Turnstile verification failed: ' . $response->get_error_message());
|
return false;
|
}
|
|
$result = json_decode(wp_remote_retrieve_body($response), true);
|
|
if (!$result['success']??false) {
|
$error_codes = implode(', ', $result['error-codes'] ?? ['Unknown error']);
|
$this->logError('verifyTurnstile', 'Turnstile verification failed: ' . $error_codes);
|
}
|
return $result['success'] ?? false;
|
}
|
}
|