1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-08-13 04:04:12 +02:00

Merge pull request #6737 from marc1706/feature/captcha_v2

[feature/captcha_v2] Refactor captcha classes and implement turnstile captcha
This commit is contained in:
Marc Alexander
2024-10-23 20:25:45 +02:00
committed by GitHub
37 changed files with 2260 additions and 259 deletions

View File

@@ -176,9 +176,8 @@ class db extends base
// Every auth module is able to define what to do by itself...
if ($show_captcha)
{
$captcha->init(CONFIRM_LOGIN);
$vc_response = $captcha->validate($row);
if ($vc_response)
$captcha->init(\phpbb\captcha\plugins\confirm_type::LOGIN);
if ($captcha->validate() !== true)
{
return array(
'status' => LOGIN_ERROR_ATTEMPTS,

View File

@@ -13,6 +13,9 @@
namespace phpbb\captcha;
use phpbb\captcha\plugins\legacy_wrapper;
use phpbb\captcha\plugins\plugin_interface;
class factory
{
/**
@@ -41,11 +44,17 @@ class factory
* Return a new instance of a given plugin
*
* @param $name
* @return object|null
* @return plugin_interface
*/
public function get_instance($name)
public function get_instance($name): plugin_interface
{
return $this->container->get($name);
$captcha = $this->container->get($name);
if ($captcha instanceof plugin_interface)
{
return $captcha;
}
return new legacy_wrapper($captcha);
}
/**
@@ -56,7 +65,7 @@ class factory
function garbage_collect($name)
{
$captcha = $this->get_instance($name);
$captcha->garbage_collect(0);
$captcha->garbage_collect();
}
/**

View File

@@ -0,0 +1,256 @@
<?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 phpbb\config\config;
use phpbb\db\driver\driver_interface;
use phpbb\language\language;
use phpbb\request\request_interface;
use phpbb\user;
abstract class base implements plugin_interface
{
/** @var config */
protected config $config;
/** @var driver_interface */
protected driver_interface $db;
/** @var language */
protected language $language;
/** @var request_interface */
protected request_interface $request;
/** @var user */
protected user $user;
/** @var int Attempts at solving the CAPTCHA */
protected int $attempts = 0;
/** @var string Stored random CAPTCHA code */
protected string $code = '';
/** @var bool Resolved state of captcha */
protected bool $solved = false;
/** @var string User supplied confirm code */
protected string $confirm_code = '';
/** @var string Confirm id hash */
protected string $confirm_id = '';
/** @var confirm_type Confirmation type */
protected confirm_type $type = confirm_type::UNDEFINED;
/** @var string Last error message */
protected string $last_error = '';
/**
* Constructor for abstract captcha base class
*
* @param config $config
* @param driver_interface $db
* @param language $language
* @param request_interface $request
* @param user $user
*/
public function __construct(config $config, driver_interface $db, language $language, request_interface $request, user $user)
{
$this->config = $config;
$this->db = $db;
$this->language = $language;
$this->request = $request;
$this->user = $user;
}
/**
* {@inheritDoc}
*/
public function init(confirm_type $type): void
{
$this->confirm_id = $this->request->variable('confirm_id', '');
$this->confirm_code = $this->request->variable('confirm_code', '');
$this->type = $type;
if (empty($this->confirm_id) || !$this->load_confirm_data())
{
// we have no confirm ID, better get ready to display something
$this->generate_confirm_data();
}
}
/**
* {@inheritDoc}
*/
public function validate(): bool
{
if ($this->confirm_id && hash_equals($this->code, $this->confirm_code))
{
$this->solved = true;
return true;
}
$this->increment_attempts();
$this->last_error = $this->language->lang('CONFIRM_CODE_WRONG');
return false;
}
/**
* {@inheritDoc}
*/
public function reset(): void
{
$sql = 'DELETE FROM ' . CONFIRM_TABLE . "
WHERE session_id = '" . $this->db->sql_escape($this->user->session_id) . "'
AND confirm_type = " . $this->type->value;
$this->db->sql_query($sql);
$this->generate_confirm_data();
}
/**
* {@inheritDoc}
*/
public function get_attempt_count(): int
{
return $this->attempts;
}
/**
* Look up attempts from confirm table
*/
protected function load_confirm_data(): bool
{
$sql = 'SELECT code, attempts
FROM ' . CONFIRM_TABLE . "
WHERE confirm_id = '" . $this->db->sql_escape($this->confirm_id) . "'
AND session_id = '" . $this->db->sql_escape($this->user->session_id) . "'
AND confirm_type = " . $this->type->value;
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);
if ($row)
{
$this->attempts = $row['attempts'];
$this->code = $row['code'];
return true;
}
return false;
}
/**
* Generate confirm data for tracking attempts
*
* @return void
*/
protected function generate_confirm_data(): void
{
$this->code = gen_rand_string_friendly(CAPTCHA_MAX_CHARS);
$this->confirm_id = md5(unique_id());
$this->attempts = 0;
$sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $this->db->sql_build_array('INSERT', array(
'confirm_id' => $this->confirm_id,
'session_id' => (string) $this->user->session_id,
'confirm_type' => $this->type->value,
'code' => $this->code,
));
$this->db->sql_query($sql);
}
/**
* Increment number of attempts for confirm ID and session
*
* @return void
*/
protected function increment_attempts(): void
{
$sql = 'UPDATE ' . CONFIRM_TABLE . "
SET attempts = attempts + 1
WHERE confirm_id = '{$this->db->sql_escape($this->confirm_id)}'
AND session_id = '{$this->db->sql_escape($this->user->session_id)}'";
$this->db->sql_query($sql);
$this->attempts++;
}
/**
* {@inheritDoc}
*/
public function get_hidden_fields(): array
{
return [
'confirm_id' => $this->confirm_id,
'confirm_code' => $this->solved === true ? $this->confirm_code : '',
];
}
/**
* {@inheritDoc}
*/
public function is_solved(): bool
{
return $this->solved;
}
/**
* {@inheritDoc}
*/
public function get_error(): string
{
return $this->last_error;
}
/**
* @inheritDoc
*/
public function garbage_collect(confirm_type $confirm_type = confirm_type::UNDEFINED): void
{
$sql = 'SELECT DISTINCT c.session_id
FROM ' . CONFIRM_TABLE . ' c
LEFT JOIN ' . SESSIONS_TABLE . ' s ON (c.session_id = s.session_id)
WHERE s.session_id IS NULL' .
((empty($confirm_type)) ? '' : ' AND c.confirm_type = ' . $confirm_type->value);
$result = $this->db->sql_query($sql);
if ($row = $this->db->sql_fetchrow($result))
{
$sql_in = [];
do
{
$sql_in[] = (string) $row['session_id'];
}
while ($row = $this->db->sql_fetchrow($result));
if (count($sql_in))
{
$sql = 'DELETE FROM ' . CONFIRM_TABLE . '
WHERE ' . $this->db->sql_in_set('session_id', $sql_in);
$this->db->sql_query($sql);
}
}
$this->db->sql_freeresult($result);
}
/**
* {@inheritDoc}
*/
public function acp_page(mixed $id, mixed $module): void
{
}
}

View File

@@ -179,16 +179,6 @@ abstract class captcha_abstract
$db->sql_freeresult($result);
}
function uninstall()
{
$this->garbage_collect(0);
}
function install()
{
return;
}
function validate()
{
global $user;

View File

@@ -0,0 +1,25 @@
<?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;
/**
* Confirmation types for CAPTCHA plugins
*/
enum confirm_type: int {
case UNDEFINED = 0;
case REGISTRATION = 1;
case LOGIN = 2;
case POST = 3;
case REPORT = 4;
}

View File

@@ -14,25 +14,34 @@
namespace phpbb\captcha\plugins;
use phpbb\config\config;
use phpbb\exception\runtime_exception;
use phpbb\db\driver\driver_interface;
use phpbb\language\language;
use phpbb\request\request_interface;
use phpbb\template\template;
use phpbb\user;
class incomplete extends captcha_abstract
class incomplete extends base
{
/**
* Constructor for incomplete captcha
*
* @param config $config
* @param driver_interface $db
* @param language $language
* @param request_interface $request
* @param template $template
* @param user $user
* @param string $phpbb_root_path
* @param string $phpEx
*/
public function __construct(protected config $config, protected template $template,
protected string $phpbb_root_path, protected string $phpEx)
{}
public function __construct(config $config, driver_interface $db, language $language, request_interface $request,
protected template $template, user $user, protected string $phpbb_root_path, protected string $phpEx)
{
parent::__construct($config, $db, $language, $request, $user);
}
/**
* @return bool True if captcha is available, false if not
* {@inheritDoc}
*/
public function is_available(): bool
{
@@ -40,70 +49,45 @@ class incomplete extends captcha_abstract
}
/**
* Dummy implementation, not supported by this captcha
*
* @throws runtime_exception
* @return void
* {@inheritDoc}
*/
public function get_generator_class(): void
public function has_config(): bool
{
throw new runtime_exception('NO_GENERATOR_CLASS');
return false;
}
/**
* Get CAPTCHA name language variable
*
* @return string Language variable
* {@inheritDoc}
*/
public static function get_name(): string
public function get_name(): string
{
return 'CAPTCHA_INCOMPLETE';
}
/**
* Init CAPTCHA
*
* @param int $type CAPTCHA type
* @return void
* {@inheritDoc}
*/
public function init($type)
public function set_name(string $name): void
{
}
/**
* Execute demo
*
* @return void
* {@inheritDoc}
*/
public function execute_demo()
public function init(confirm_type $type): void
{
}
/**
* Execute CAPTCHA
*
* @return void
* {@inheritDoc}
*/
public function execute()
{
}
/**
* Get template data for demo
*
* @param int|string $id ACP module ID
*
* @return string Demo template file name
*/
public function get_demo_template($id): string
public function get_demo_template(): string
{
return '';
}
/**
* Get template data for CAPTCHA
*
* @return string CAPTCHA template file name
* {@inheritDoc}
*/
public function get_template(): string
{
@@ -118,9 +102,7 @@ class incomplete extends captcha_abstract
}
/**
* Validate CAPTCHA
*
* @return false Incomplete CAPTCHA will never validate
* {@inheritDoc}
*/
public function validate(): bool
{
@@ -128,12 +110,26 @@ class incomplete extends captcha_abstract
}
/**
* Check whether CAPTCHA is solved
*
* @return false Incomplete CAPTCHA will never be solved
* {@inheritDoc}
*/
public function get_error(): string
{
return '';
}
/**
* {@inheritDoc}
*/
public function is_solved(): bool
{
return false;
}
/**
* {@inheritDoc}
*/
public function get_attempt_count(): int
{
return 0;
}
}

View File

@@ -0,0 +1,221 @@
<?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;
class legacy_wrapper implements plugin_interface
{
/** @var object Legacy CAPTCHA instance, should implement functionality as required in phpBB 3.3 */
private $legacy_captcha;
/** @var string Last error */
private string $last_error;
/**
* Constructor for legacy CAPTCHA wrapper
*
* @param object $legacy_captcha
*/
public function __construct(object $legacy_captcha)
{
$this->legacy_captcha = $legacy_captcha;
}
/**
* {@inheritDoc}
*/
public function is_available(): bool
{
if (method_exists($this->legacy_captcha, 'is_available'))
{
return $this->legacy_captcha->is_available();
}
return false;
}
/**
* {@inheritDoc}
*/
public function has_config(): bool
{
if (method_exists($this->legacy_captcha, 'has_config'))
{
return $this->legacy_captcha->has_config();
}
return false;
}
/**
* {@inheritDoc}
*/
public function get_name(): string
{
if (method_exists($this->legacy_captcha, 'get_name'))
{
return $this->legacy_captcha->get_name();
}
return '';
}
/**
* {@inheritDoc}
*/
public function set_name(string $name): void
{
if (method_exists($this->legacy_captcha, 'set_name'))
{
$this->legacy_captcha->set_name($name);
}
}
/**
* {@inheritDoc}
*/
public function init(confirm_type $type): void
{
if (method_exists($this->legacy_captcha, 'init'))
{
$this->legacy_captcha->init($type->value);
}
}
/**
* {@inheritDoc}
*/
public function get_hidden_fields(): array
{
if (method_exists($this->legacy_captcha, 'get_hidden_fields'))
{
return $this->legacy_captcha->get_hidden_fields();
}
return [];
}
/**
* {@inheritDoc}
*/
public function validate(): bool
{
if (method_exists($this->legacy_captcha, 'validate'))
{
$error = $this->legacy_captcha->validate();
if ($error)
{
$this->last_error = $error;
return false;
}
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public function get_error(): string
{
return $this->last_error;
}
/**
* {@inheritDoc}
*/
public function is_solved(): bool
{
if (method_exists($this->legacy_captcha, 'is_solved'))
{
return $this->legacy_captcha->is_solved();
}
return false;
}
/**
* {@inheritDoc}
*/
public function reset(): void
{
if (method_exists($this->legacy_captcha, 'reset'))
{
$this->legacy_captcha->reset();
}
}
/**
* {@inheritDoc}
*/
public function get_attempt_count(): int
{
if (method_exists($this->legacy_captcha, 'get_attempt_count'))
{
return $this->legacy_captcha->get_attempt_count();
}
// Ensure this is deemed as too many attempts
return PHP_INT_MAX;
}
/**
* {@inheritDoc}
*/
public function get_template(): string
{
if (method_exists($this->legacy_captcha, 'get_template'))
{
return $this->legacy_captcha->get_template();
}
return '';
}
/**
* {@inheritDoc}
*/
public function get_demo_template(): string
{
if (method_exists($this->legacy_captcha, 'get_demo_template'))
{
return $this->legacy_captcha->get_demo_template(0);
}
return '';
}
/**
* {@inheritDoc}
*/
public function garbage_collect(confirm_type $confirm_type = confirm_type::UNDEFINED): void
{
if (method_exists($this->legacy_captcha, 'garbage_collect'))
{
$this->legacy_captcha->garbage_collect($confirm_type->value);
}
}
/**
* {@inheritDoc}
*/
public function acp_page(mixed $id, mixed $module): void
{
if (method_exists($this->legacy_captcha, 'acp_page'))
{
$this->legacy_captcha->acp_page($id, $module);
}
}
}

View File

@@ -0,0 +1,126 @@
<?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;
interface plugin_interface
{
/**
* Check if the plugin is available
*
* @return bool True if the plugin is available, false if not
*/
public function is_available(): bool;
/**
* Check if the plugin has a configuration
*
* @return bool True if the plugin has a configuration, false if not
*/
public function has_config(): bool;
/**
* Get the name of the plugin, should be language variable
*
* @return string
*/
public function get_name(): string;
/**
* Set the service name of the plugin
*
* @param string $name
*/
public function set_name(string $name): void;
/**
* Display the captcha for the specified type
*
* @param confirm_type $type Type of captcha, should be one of the CONFIRMATION_* constants
* @return void
*/
public function init(confirm_type $type): void;
/**
* Get hidden form fields for this captcha plugin
*
* @return array Hidden form fields
*/
public function get_hidden_fields(): array;
/**
* Validate the captcha with the given request data
*
* @return bool True if request data was valid captcha reply, false if not
*/
public function validate(): bool;
/**
* Get error string from captcha
*
* @return string Error string, empty string if there is no error
*/
public function get_error(): string;
/**
* Return whether captcha was solved
*
* @return bool True if captcha was solved, false if not
*/
public function is_solved(): bool;
/**
* Reset captcha state, e.g. after checking if it's valid
*
* @return void
*/
public function reset(): void;
/**
* Get attempt count for this captcha and user
*
* @return int Number of attempts
*/
public function get_attempt_count(): int;
/**
* Get template filename for captcha
*
* @return string Template file name
*/
public function get_template(): string;
/**
* Get template filename for demo
*
* @return string Demo template file name
*/
public function get_demo_template(): string;
/**
* Garbage collect captcha plugin
*
* @param confirm_type $confirm_type Confirm type to garbage collect, defaults to all (0)
* @return void
*/
public function garbage_collect(confirm_type $confirm_type = confirm_type::UNDEFINED): void;
/**
* Display acp page
*
* @param mixed $id ACP module id
* @param mixed $module ACP module name
* @return void
*/
public function acp_page(mixed $id, mixed $module): void;
}

View File

@@ -40,7 +40,7 @@ class qa
protected $service_name;
/** @var int Question ID */
protected $question = -1;
private $question = -1;
/**
* Constructor
@@ -323,71 +323,6 @@ class qa
$db->sql_freeresult($result);
}
/**
* API function - we don't drop the tables here, as that would cause the loss of all entered questions.
*/
function uninstall()
{
$this->garbage_collect(0);
}
/**
* API function - set up shop
*/
function install()
{
global $phpbb_container;
$db_tool = $phpbb_container->get('dbal.tools');
$schemas = array(
$this->table_captcha_questions => array (
'COLUMNS' => array(
'question_id' => array('UINT', null, 'auto_increment'),
'strict' => array('BOOL', 0),
'lang_id' => array('UINT', 0),
'lang_iso' => array('VCHAR:30', ''),
'question_text' => array('TEXT_UNI', ''),
),
'PRIMARY_KEY' => 'question_id',
'KEYS' => array(
'lang' => array('INDEX', 'lang_iso'),
),
),
$this->table_captcha_answers => array (
'COLUMNS' => array(
'question_id' => array('UINT', 0),
'answer_text' => array('STEXT_UNI', ''),
),
'KEYS' => array(
'qid' => array('INDEX', 'question_id'),
),
),
$this->table_qa_confirm => array (
'COLUMNS' => array(
'session_id' => array('CHAR:32', ''),
'confirm_id' => array('CHAR:32', ''),
'lang_iso' => array('VCHAR:30', ''),
'question_id' => array('UINT', 0),
'attempts' => array('UINT', 0),
'confirm_type' => array('USINT', 0),
),
'KEYS' => array(
'session_id' => array('INDEX', 'session_id'),
'lookup' => array('INDEX', array('confirm_id', 'session_id', 'lang_iso')),
),
'PRIMARY_KEY' => 'confirm_id',
),
);
foreach ($schemas as $table => $schema)
{
if (!$db_tool->sql_table_exists($table))
{
$db_tool->sql_create_table($table, $schema);
}
}
}
/**
* API function - see what has to be done to validate
*/
@@ -647,11 +582,6 @@ class qa
$user->add_lang('acp/board');
$user->add_lang('captcha_qa');
if (!self::is_installed())
{
$this->install();
}
$module->tpl_name = 'captcha_qa_acp';
$module->page_title = 'ACP_VC_SETTINGS';
$form_key = 'acp_captcha';

View File

@@ -179,16 +179,6 @@ class recaptcha extends captcha_abstract
return $hidden_fields;
}
function uninstall()
{
$this->garbage_collect(0);
}
function install()
{
return;
}
function validate()
{
if (!parent::validate())

View File

@@ -0,0 +1,287 @@
<?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->user->ip,
];
// 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(mixed $id, mixed $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,
));
}
}
}

View File

@@ -0,0 +1,89 @@
<?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\db\migration\data\v400;
use phpbb\db\migration\migration;
class qa_captcha extends migration
{
public function effectively_installed(): bool
{
return $this->db_tools->sql_table_exists($this->tables['captcha_qa_questions'])
&& $this->db_tools->sql_table_exists($this->tables['captcha_qa_answers'])
&& $this->db_tools->sql_table_exists($this->tables['captcha_qa_confirm']);
}
public static function depends_on(): array
{
return [
'\phpbb\db\migration\data\v400\dev',
];
}
public function update_schema(): array
{
return [
'add_tables' => [
$this->tables['captcha_qa_questions'] => [
'COLUMNS' => [
'question_id' => ['UINT', null, 'auto_increment'],
'strict' => ['BOOL', 0],
'lang_id' => ['UINT', 0],
'lang_iso' => ['VCHAR:30', ''],
'question_text' => ['TEXT_UNI', ''],
],
'PRIMARY_KEY' => 'question_id',
'KEYS' => [
'lang' => ['INDEX', 'lang_iso'],
],
],
$this->tables['captcha_qa_answers'] => [
'COLUMNS' => [
'question_id' => ['UINT', 0],
'answer_text' => ['STEXT_UNI', ''],
],
'KEYS' => [
'qid' => ['INDEX', 'question_id'],
],
],
$this->tables['captcha_qa_confirm'] => [
'COLUMNS' => [
'session_id' => ['CHAR:32', ''],
'confirm_id' => ['CHAR:32', ''],
'lang_iso' => ['VCHAR:30', ''],
'question_id' => ['UINT', 0],
'attempts' => ['UINT', 0],
'confirm_type' => ['USINT', 0],
],
'KEYS' => [
'session_id' => ['INDEX', 'session_id'],
'lookup' => ['INDEX', ['confirm_id', 'session_id', 'lang_iso']],
],
'PRIMARY_KEY' => 'confirm_id',
],
],
];
}
public function revert_schema(): array
{
return [
'drop_tables' => [
$this->tables['captcha_qa_questions'],
$this->tables['captcha_qa_answers'],
$this->tables['captcha_qa_confirm']
],
];
}
}

View File

@@ -0,0 +1,51 @@
<?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\db\migration\data\v400;
use phpbb\db\migration\migration;
class turnstile_captcha extends migration
{
public function effectively_installed(): bool
{
return $this->config->offsetExists('captcha_turnstile_sitekey')
&& $this->config->offsetExists('captcha_turnstile_secret')
&& $this->config->offsetExists('captcha_turnstile_theme');
}
public static function depends_on(): array
{
return [
'\phpbb\db\migration\data\v400\dev',
];
}
public function update_data(): array
{
return [
['config.add', ['captcha_turnstile_sitekey', '']],
['config.add', ['captcha_turnstile_secret', '']],
['config.add', ['captcha_turnstile_theme', 'light']],
];
}
public function revert_data(): array
{
return [
['config.remove', ['captcha_turnstile_sitekey']],
['config.remove', ['captcha_turnstile_secret']],
['config.remove', ['captcha_turnstile_theme']],
];
}
}

View File

@@ -403,6 +403,7 @@ class language
$this->lang['USER_LANG'] = $lang_values['user_lang'] ?? 'en-gb';
$this->lang['PLURAL_RULE'] = $lang_values['plural_rule'] ?? 1;
$this->lang['RECAPTCHA_LANG'] = $lang_values['recaptcha_lang'] ?? 'en-GB';
$this->lang['TURNSTILE_LANG'] = $lang_values['turnstile_lang'] ?? 'auto'; // default to auto mode
}
/**

View File

@@ -110,16 +110,17 @@ class language_file_helper
}
return [
'iso' => $data['extra']['language-iso'],
'name' => $data['extra']['english-name'],
'local_name' => $data['extra']['local-name'],
'author' => implode(', ', $authors),
'version' => $data['version'],
'phpbb_version' => $data['extra']['phpbb-version'],
'direction' => $data['extra']['direction'],
'user_lang' => $data['extra']['user-lang'],
'plural_rule' => $data['extra']['plural-rule'],
'recaptcha_lang'=> $data['extra']['recaptcha-lang'],
'iso' => $data['extra']['language-iso'],
'name' => $data['extra']['english-name'],
'local_name' => $data['extra']['local-name'],
'author' => implode(', ', $authors),
'version' => $data['version'],
'phpbb_version' => $data['extra']['phpbb-version'],
'direction' => $data['extra']['direction'],
'user_lang' => $data['extra']['user-lang'],
'plural_rule' => $data['extra']['plural-rule'],
'recaptcha_lang' => $data['extra']['recaptcha-lang'],
'turnstile_lang' => $data['extra']['turnstile-lang'] ?? '',
];
}
}

View File

@@ -13,6 +13,8 @@
namespace phpbb\report\controller;
use phpbb\captcha\plugins\confirm_type;
use phpbb\captcha\plugins\plugin_interface;
use phpbb\exception\http_exception;
use phpbb\report\report_handler_interface;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -131,7 +133,7 @@ class report
if ($this->config['enable_post_confirm'] && !$this->user->data['is_registered'])
{
$captcha = $this->captcha_factory->get_instance($this->config['captcha_plugin']);
$captcha->init(CONFIRM_REPORT);
$captcha->init(confirm_type::REPORT);
}
//Has the report been cancelled?
@@ -140,7 +142,7 @@ class report
return new RedirectResponse($redirect_url, 302);
}
// Check CAPTCHA, if the form was submited
// Check CAPTCHA, if the form was submitted
if (!empty($submit) && isset($captcha))
{
$captcha_template_array = $this->check_captcha($captcha);
@@ -298,18 +300,17 @@ class report
/**
* Check CAPTCHA
*
* @param object $captcha A phpBB CAPTCHA object
* @param plugin_interface $captcha A phpBB CAPTCHA object
* @return array template variables which ensures that CAPTCHA's work correctly
*/
protected function check_captcha($captcha)
protected function check_captcha(plugin_interface $captcha)
{
$error = array();
$captcha_hidden_fields = '';
$visual_confirmation_response = $captcha->validate();
if ($visual_confirmation_response)
if ($captcha->validate() !== true)
{
$error[] = $visual_confirmation_response;
$error[] = $captcha->get_error();
}
if (count($error) === 0)

View File

@@ -183,6 +183,7 @@ class forms extends AbstractExtension
'GROUP_ONLY' => (bool) ($form_data['group_only'] ?? false),
'SIZE' => (int) ($form_data['size'] ?? 0),
'MULTIPLE' => (bool) ($form_data['multiple'] ?? false),
'ONCHANGE' => (string) ($form_data['onchange'] ?? ''),
]);
}
catch (\Twig\Error\Error $e)