mirror of
https://github.com/phpbb/phpbb.git
synced 2025-07-24 18:41:52 +02:00
289 lines
6.8 KiB
PHP
289 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
*
|
|
* This file is part of the phpBB Forum Software package.
|
|
*
|
|
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
|
* @license GNU General Public License, version 2 (GPL-2.0)
|
|
*
|
|
* For full copyright and license information, please see
|
|
* the docs/CREDITS.txt file.
|
|
*
|
|
*/
|
|
|
|
namespace phpbb\captcha\plugins;
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\Exception\GuzzleException;
|
|
use phpbb\config\config;
|
|
use phpbb\db\driver\driver_interface;
|
|
use phpbb\language\language;
|
|
use phpbb\log\log_interface;
|
|
use phpbb\request\request_interface;
|
|
use phpbb\template\template;
|
|
use phpbb\user;
|
|
|
|
class turnstile extends base
|
|
{
|
|
/** @var string URL to cloudflare turnstile API javascript */
|
|
private const SCRIPT_URL = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
|
|
|
|
/** @var string API endpoint for turnstile verification */
|
|
private const VERIFY_ENDPOINT = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
|
|
|
|
/** @var Client */
|
|
protected Client $client;
|
|
|
|
/** @var language */
|
|
protected language $language;
|
|
|
|
/** @var log_interface */
|
|
protected log_interface $log;
|
|
|
|
/** @var template */
|
|
protected template $template;
|
|
|
|
/** @var string Service name */
|
|
protected string $service_name = '';
|
|
|
|
/** @var array|string[] Supported themes for Turnstile CAPTCHA */
|
|
protected static array $supported_themes = [
|
|
'light',
|
|
'dark',
|
|
'auto'
|
|
];
|
|
|
|
/**
|
|
* Constructor for turnstile captcha plugin
|
|
*
|
|
* @param config $config
|
|
* @param driver_interface $db
|
|
* @param language $language
|
|
* @param log_interface $log
|
|
* @param request_interface $request
|
|
* @param template $template
|
|
* @param user $user
|
|
*/
|
|
public function __construct(config $config, driver_interface $db, language $language, log_interface $log, request_interface $request, template $template, user $user)
|
|
{
|
|
parent::__construct($config, $db, $language, $request, $user);
|
|
|
|
$this->language = $language;
|
|
$this->log = $log;
|
|
$this->template = $template;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function is_available(): bool
|
|
{
|
|
$this->init($this->type);
|
|
|
|
return !empty($this->config->offsetGet('captcha_turnstile_sitekey'))
|
|
&& !empty($this->config->offsetGet('captcha_turnstile_secret'));
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function has_config(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function get_name(): string
|
|
{
|
|
return 'CAPTCHA_TURNSTILE';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function set_name(string $name): void
|
|
{
|
|
$this->service_name = $name;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function init(confirm_type $type): void
|
|
{
|
|
parent::init($type);
|
|
|
|
$this->language->add_lang('captcha_turnstile');
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function validate(): bool
|
|
{
|
|
if (parent::validate())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
$turnstile_response = $this->request->variable('cf-turnstile-response', '');
|
|
if (!$turnstile_response)
|
|
{
|
|
// Return without checking against server without a turnstile response
|
|
return false;
|
|
}
|
|
|
|
// Retrieve form data for verification
|
|
$form_data = [
|
|
'secret' => $this->config['captcha_turnstile_secret'],
|
|
'response' => $turnstile_response,
|
|
'remoteip' => $this->request->header('CF-Connecting-IP'),
|
|
//'idempotency_key' => $this->confirm_id, // check if we need this
|
|
];
|
|
|
|
// Create guzzle client
|
|
$client = $this->get_client();
|
|
|
|
// Check captcha with turnstile API
|
|
try
|
|
{
|
|
$response = $client->request('POST', self::VERIFY_ENDPOINT, [
|
|
'form_params' => $form_data,
|
|
]);
|
|
}
|
|
catch (GuzzleException)
|
|
{
|
|
// Something went wrong during the request to Cloudflare, assume captcha was bad
|
|
$this->solved = false;
|
|
return false;
|
|
}
|
|
|
|
// Decode the JSON response
|
|
$result = json_decode($response->getBody(), true);
|
|
|
|
// Check if the response indicates success
|
|
if (isset($result['success']) && $result['success'] === true)
|
|
{
|
|
$this->solved = true;
|
|
$this->confirm_code = $this->code;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$this->last_error = $this->language->lang('CAPTCHA_TURNSTILE_INCORRECT');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get Guzzle client
|
|
*
|
|
* @return Client
|
|
*/
|
|
protected function get_client(): Client
|
|
{
|
|
if (!isset($this->client))
|
|
{
|
|
$this->client = new Client();
|
|
}
|
|
|
|
return $this->client;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function get_template(): string
|
|
{
|
|
if ($this->is_solved())
|
|
{
|
|
return '';
|
|
}
|
|
|
|
$this->template->assign_vars([
|
|
'S_TURNSTILE_AVAILABLE' => $this->is_available(),
|
|
'TURNSTILE_SITEKEY' => $this->config->offsetGet('captcha_turnstile_sitekey'),
|
|
'TURNSTILE_THEME' => $this->config->offsetGet('captcha_turnstile_theme'),
|
|
'U_TURNSTILE_SCRIPT' => self::SCRIPT_URL,
|
|
'CONFIRM_TYPE_REGISTRATION' => $this->type->value,
|
|
]);
|
|
|
|
return 'captcha_turnstile.html';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function get_demo_template(): string
|
|
{
|
|
$this->template->assign_vars([
|
|
'TURNSTILE_THEME' => $this->config->offsetGet('captcha_turnstile_theme'),
|
|
'U_TURNSTILE_SCRIPT' => self::SCRIPT_URL,
|
|
]);
|
|
|
|
return 'captcha_turnstile_acp_demo.html';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function acp_page($id, $module): void
|
|
{
|
|
$captcha_vars = [
|
|
'captcha_turnstile_sitekey' => 'CAPTCHA_TURNSTILE_SITEKEY',
|
|
'captcha_turnstile_secret' => 'CAPTCHA_TURNSTILE_SECRET',
|
|
];
|
|
|
|
$module->tpl_name = 'captcha_turnstile_acp';
|
|
$module->page_title = 'ACP_VC_SETTINGS';
|
|
$form_key = 'acp_captcha';
|
|
add_form_key($form_key);
|
|
|
|
$submit = $this->request->is_set_post('submit');
|
|
|
|
if ($submit && check_form_key($form_key))
|
|
{
|
|
$captcha_vars = array_keys($captcha_vars);
|
|
foreach ($captcha_vars as $captcha_var)
|
|
{
|
|
$value = $this->request->variable($captcha_var, '');
|
|
if ($value)
|
|
{
|
|
$this->config->set($captcha_var, $value);
|
|
}
|
|
}
|
|
|
|
$captcha_theme = $this->request->variable('captcha_turnstile_theme', self::$supported_themes[0]);
|
|
if (in_array($captcha_theme, self::$supported_themes))
|
|
{
|
|
$this->config->set('captcha_turnstile_theme', $captcha_theme);
|
|
}
|
|
|
|
$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_CONFIG_VISUAL');
|
|
trigger_error($this->language->lang('CONFIG_UPDATED') . adm_back_link($module->u_action));
|
|
}
|
|
else if ($submit)
|
|
{
|
|
trigger_error($this->language->lang('FORM_INVALID') . adm_back_link($module->u_action));
|
|
}
|
|
else
|
|
{
|
|
foreach ($captcha_vars as $captcha_var => $template_var)
|
|
{
|
|
$var = $this->request->is_set($captcha_var) ? $this->request->variable($captcha_var, '') : $this->config->offsetGet($captcha_var);;
|
|
$this->template->assign_var($template_var, $var);
|
|
}
|
|
|
|
$this->template->assign_vars(array(
|
|
'CAPTCHA_PREVIEW' => $this->get_demo_template(),
|
|
'CAPTCHA_NAME' => $this->service_name,
|
|
'CAPTCHA_TURNSTILE_THEME' => $this->config->offsetGet('captcha_turnstile_theme'),
|
|
'CAPTCHA_TURNSTILE_THEMES' => self::$supported_themes,
|
|
'U_ACTION' => $module->u_action,
|
|
));
|
|
}
|
|
}
|
|
}
|