mirror of
https://github.com/phpbb/phpbb.git
synced 2025-06-10 13:28:55 +00: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:
commit
4985a27dcf
37 changed files with 2260 additions and 259 deletions
|
@ -1,79 +1,85 @@
|
|||
<!-- INCLUDE overall_header.html -->
|
||||
{% include 'overall_header.html' %}
|
||||
|
||||
<a id="maincontent"></a>
|
||||
|
||||
<h1>{L_ACP_VC_SETTINGS}</h1>
|
||||
<h1>{{ lang('ACP_VC_SETTINGS') }}</h1>
|
||||
|
||||
<p>{L_ACP_VC_SETTINGS_EXPLAIN}</p>
|
||||
<p>{{ lang('ACP_VC_SETTINGS_EXPLAIN') }}</p>
|
||||
|
||||
<p>{L_ACP_VC_EXT_GET_MORE}</p>
|
||||
<p>{{ lang('ACP_VC_EXT_GET_MORE') }}</p>
|
||||
|
||||
<!-- IF ERROR_MSG -->
|
||||
{% if ERRORS %}
|
||||
<div class="errorbox">
|
||||
<h3>{L_WARNING}</h3>
|
||||
<p>{ERROR_MSG}</p>
|
||||
<h3>{{ lang('WARNING') }}</h3>
|
||||
<p>{{ ERRORS|join('<br>') }}</p>
|
||||
</div>
|
||||
<!-- ENDIF -->
|
||||
{% endif %}
|
||||
|
||||
<form id="acp_captcha" method="post" action="{U_ACTION}">
|
||||
<form id="acp_captcha" method="post" action="{{ U_ACTION }}">
|
||||
|
||||
<fieldset>
|
||||
<legend>{L_GENERAL_OPTIONS}</legend>
|
||||
<legend>{{ lang('GENERAL_OPTIONS') }}</legend>
|
||||
|
||||
<dl>
|
||||
<dt><label for="enable_confirm">{L_VISUAL_CONFIRM_REG}{L_COLON}</label><br /><span>{L_VISUAL_CONFIRM_REG_EXPLAIN}</span></dt>
|
||||
<dd><label><input type="radio" class="radio" id="enable_confirm" name="enable_confirm" value="1"<!-- IF REG_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_ENABLED}</label>
|
||||
<label><input type="radio" class="radio" name="enable_confirm" value="0"<!-- IF not REG_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_DISABLED}</label></dd>
|
||||
<dt><label for="enable_confirm">{{ lang('VISUAL_CONFIRM_REG') ~ lang('COLON') }}</label><br /><span>{{ lang('VISUAL_CONFIRM_REG_EXPLAIN') }}</span></dt>
|
||||
<dd>
|
||||
<label><input type="radio" class="radio" id="enable_confirm" name="enable_confirm" value="1"{% if REG_ENABLE %} checked="checked"{% endif %}/> {{ lang('ENABLED') }}</label>
|
||||
<label><input type="radio" class="radio" name="enable_confirm" value="0"{% if not REG_ENABLE %} checked="checked"{% endif %}/> {{ lang('DISABLED') }}</label>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="max_reg_attempts">{L_REG_LIMIT}{L_COLON}</label><br /><span>{L_REG_LIMIT_EXPLAIN}</span></dt>
|
||||
<dd><input id="max_reg_attempts" type="number" min="0" max="9999" name="max_reg_attempts" value="{REG_LIMIT}" /></dd>
|
||||
<dt><label for="max_reg_attempts">{{ lang('REG_LIMIT') ~ lang('COLON') }}</label><br /><span>{{ lang('REG_LIMIT_EXPLAIN') }}</span></dt>
|
||||
<dd><input id="max_reg_attempts" type="number" min="0" max="9999" name="max_reg_attempts" value="{{ REG_LIMIT }}" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="max_login_attempts">{L_MAX_LOGIN_ATTEMPTS}{L_COLON}</label><br /><span>{L_MAX_LOGIN_ATTEMPTS_EXPLAIN}</span></dt>
|
||||
<dd><input id="max_login_attempts" type="number" min="0" max="9999" name="max_login_attempts" value="{MAX_LOGIN_ATTEMPTS}" /></dd>
|
||||
<dt><label for="max_login_attempts">{{ lang('MAX_LOGIN_ATTEMPTS') ~ lang('COLON') }}</label><br /><span>{{ lang('MAX_LOGIN_ATTEMPTS_EXPLAIN') }}</span></dt>
|
||||
<dd><input id="max_login_attempts" type="number" min="0" max="9999" name="max_login_attempts" value="{{ MAX_LOGIN_ATTEMPTS }}" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="enable_post_confirm">{L_VISUAL_CONFIRM_POST}{L_COLON}</label><br /><span>{L_VISUAL_CONFIRM_POST_EXPLAIN}</span></dt>
|
||||
<dd><label><input type="radio" class="radio" id="enable_post_confirm" name="enable_post_confirm" value="1"<!-- IF POST_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_ENABLED}</label>
|
||||
<label><input type="radio" class="radio" name="enable_post_confirm" value="0"<!-- IF not POST_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_DISABLED}</label></dd>
|
||||
<dt><label for="enable_post_confirm">{{ lang('VISUAL_CONFIRM_POST') ~ lang('COLON') }}</label><br /><span>{{ lang('VISUAL_CONFIRM_POST_EXPLAIN') }}</span></dt>
|
||||
<dd>
|
||||
<label><input type="radio" class="radio" id="enable_post_confirm" name="enable_post_confirm" value="1"{% if POST_ENABLE %} checked="checked"{% endif %}/> {{ lang('ENABLED') }}</label>
|
||||
<label><input type="radio" class="radio" name="enable_post_confirm" value="0"{% if not POST_ENABLE %} checked="checked"{% endif %}/> {{ lang('DISABLED') }}</label>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="confirm_refresh">{L_VISUAL_CONFIRM_REFRESH}{L_COLON}</label><br /><span>{L_VISUAL_CONFIRM_REFRESH_EXPLAIN}</span></dt>
|
||||
<dd><label><input type="radio" class="radio" id="confirm_refresh" name="confirm_refresh" value="1"<!-- IF CONFIRM_REFRESH --> checked="checked"<!-- ENDIF --> /> {L_ENABLED}</label>
|
||||
<label><input type="radio" class="radio" name="confirm_refresh" value="0"<!-- IF not CONFIRM_REFRESH --> checked="checked"<!-- ENDIF --> /> {L_DISABLED}</label></dd>
|
||||
<dt><label for="confirm_refresh">{{ lang('VISUAL_CONFIRM_REFRESH') ~ lang('COLON') }}</label><br /><span>{{ lang('VISUAL_CONFIRM_REFRESH_EXPLAIN') }}</span></dt>
|
||||
<dd>
|
||||
<label><input type="radio" class="radio" id="confirm_refresh" name="confirm_refresh" value="1"{% if CONFIRM_REFRESH %} checked="checked"{% endif %}/> {{ lang('ENABLED') }}</label>
|
||||
<label><input type="radio" class="radio" name="confirm_refresh" value="0"{% if not CONFIRM_REFRESH %} checked="checked"{% endif %}/> {{ lang('DISABLED') }}</label>
|
||||
</dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{L_AVAILABLE_CAPTCHAS}</legend>
|
||||
<legend>{{ lang('AVAILABLE_CAPTCHAS') }}</legend>
|
||||
<dl>
|
||||
<dt><label for="captcha_select">{L_CAPTCHA_SELECT}{L_COLON}</label><br /><span>{L_CAPTCHA_SELECT_EXPLAIN}</span></dt>
|
||||
<dd><select id="captcha_select" name="select_captcha" onchange="(document.getElementById('acp_captcha')).submit()" >{CAPTCHA_SELECT}</select></dd>
|
||||
<dt><label for="captcha_select">{{ lang('CAPTCHA_SELECT') ~ lang('COLON') }}</label><br><span>{{ lang('CAPTCHA_SELECT_EXPLAIN') }}</span></dt>
|
||||
<dd>{{ FormsSelect(CAPTCHA_SELECT | merge({id: 'captcha_select', onchange: "(document.getElementById('acp_captcha')).submit()"})) }}</dd>
|
||||
</dl>
|
||||
<!-- IF S_CAPTCHA_HAS_CONFIG -->
|
||||
{% if S_CAPTCHA_HAS_CONFIG %}
|
||||
<dl>
|
||||
<dt><label for="configure">{L_CAPTCHA_CONFIGURE}{L_COLON}</label><br /><span>{L_CAPTCHA_CONFIGURE_EXPLAIN}</span></dt>
|
||||
<dd><input class="button2" type="submit" id="configure" name="configure" value="{L_CONFIGURE}" /></dd>
|
||||
<dt><label for="configure">{{ lang('CAPTCHA_CONFIGURE') ~ lang('COLON') }}</label><br /><span>{{ lang('CAPTCHA_CONFIGURE_EXPLAIN') }}</span></dt>
|
||||
<dd><input class="button2" type="submit" id="configure" name="configure" value="{{ lang('CONFIGURE') }}" /></dd>
|
||||
</dl>
|
||||
<!-- ENDIF -->
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
|
||||
<!-- IF CAPTCHA_PREVIEW_TPL -->
|
||||
{% if CAPTCHA_PREVIEW_TPL %}
|
||||
<fieldset>
|
||||
<legend>{L_PREVIEW}</legend>
|
||||
<!-- INCLUDE {CAPTCHA_PREVIEW_TPL} -->
|
||||
<legend>{{ lang('PREVIEW') }}</legend>
|
||||
{% include CAPTCHA_PREVIEW_TPL %}
|
||||
</fieldset>
|
||||
<!-- ENDIF -->
|
||||
{% endif %}
|
||||
|
||||
<fieldset>
|
||||
<legend>{L_ACP_SUBMIT_CHANGES}</legend>
|
||||
<legend>{{ lang('ACP_SUBMIT_CHANGES') }}</legend>
|
||||
<p class="submit-buttons">
|
||||
<input class="button1" type="submit" id="main_submit" name="main_submit" value="{L_SUBMIT}" />
|
||||
<input class="button2" type="reset" id="form_reset" name="reset" value="{L_RESET}" />
|
||||
<input class="button1" type="submit" id="main_submit" name="main_submit" value="{{ lang('SUBMIT') }}" />
|
||||
<input class="button2" type="reset" id="form_reset" name="reset" value="{{ lang('RESET') }}" />
|
||||
</p>
|
||||
{S_FORM_TOKEN}
|
||||
{{ S_FORM_TOKEN }}
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<!-- INCLUDE overall_footer.html -->
|
||||
{% include 'overall_footer.html' %}
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
<tr>
|
||||
<th colspan="3">{L_QUESTIONS}</th>
|
||||
</tr>
|
||||
{% if questions %}
|
||||
<tr class="row3">
|
||||
<td style="text-align: center;">{L_QUESTION_TEXT}</td>
|
||||
<td style="width: 5%; text-align: center;">{L_QUESTION_LANG}</td>
|
||||
<td style="vertical-align: top; width: 50px; text-align: center; white-space: nowrap;">{L_ACTION}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
@ -33,6 +35,10 @@
|
|||
<td style="text-align: center;">{{ question.QUESTION_LANG }}</td>
|
||||
<td style="text-align: center;"><a href="{{ question.U_EDIT }}">{{ ICON_EDIT }}</a> <a href="{{ question.U_DELETE }}">{{ ICON_DELETE }}</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td class="row3 centered-text" colspan="3">{{ lang('QA_NO_QUESTIONS') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
|
|
64
phpBB/adm/style/captcha_turnstile_acp.html
Normal file
64
phpBB/adm/style/captcha_turnstile_acp.html
Normal file
|
@ -0,0 +1,64 @@
|
|||
{% include('overall_header.html') %}
|
||||
|
||||
<a id="maincontent"></a>
|
||||
|
||||
<h1>{{ lang('ACP_VC_SETTINGS') }}</h1>
|
||||
|
||||
<p>{{ lang('ACP_VC_SETTINGS_EXPLAIN') }}</p>
|
||||
|
||||
<form id="acp_captcha" method="post" action="{{ U_ACTION }}">
|
||||
<fieldset>
|
||||
<legend>{{ lang('GENERAL_OPTIONS') }}</legend>
|
||||
<dl>
|
||||
<dt>
|
||||
<label for="captcha_turnstile_sitekey">{{ lang('CAPTCHA_TURNSTILE_SITEKEY') ~ lang('COLON') }}</label><br>
|
||||
<span>{{ lang('CAPTCHA_TURNSTILE_SITEKEY_EXPLAIN') }}</span>
|
||||
</dt>
|
||||
<dd><input id="captcha_turnstile_sitekey" name="captcha_turnstile_sitekey" value="{{ CAPTCHA_TURNSTILE_SITEKEY }}" size="50" type="text" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>
|
||||
<label for="captcha_turnstile_secret">{{ lang('CAPTCHA_TURNSTILE_SECRET') ~ lang('COLON') }}</label><br>
|
||||
<span>{{ lang('CAPTCHA_TURNSTILE_SECRET_EXPLAIN') }}</span>
|
||||
</dt>
|
||||
<dd><input id="captcha_turnstile_secret" name="captcha_turnstile_secret" value="{{ CAPTCHA_TURNSTILE_SECRET }}" size="50" type="text" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>
|
||||
<label>{{ lang('CAPTCHA_TURNSTILE_THEME') ~ lang('COLON') }}</label>
|
||||
<br><span>{{ lang('CAPTCHA_TURNSTILE_THEME_EXPLAIN') }}</span>
|
||||
</dt>
|
||||
<dd>
|
||||
{% for theme in CAPTCHA_TURNSTILE_THEMES %}
|
||||
<label>
|
||||
<input class="radio" name="captcha_turnstile_theme" type="radio" value="{{ theme }}"{{ theme == CAPTCHA_TURNSTILE_THEME ? ' checked' }}>
|
||||
<span>{{ lang('CAPTCHA_TURNSTILE_THEME_' ~ theme|upper) }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{{ lang('PREVIEW') }}</legend>
|
||||
{% if PREVIEW %}
|
||||
<div class="successbox">
|
||||
<h3>{{ lang('WARNING') }}</h3>
|
||||
<p>{{ lang('CAPTCHA_PREVIEW_MSG') }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include(CAPTCHA_PREVIEW) %}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{ lang('ACP_SUBMIT_CHANGES') }}</legend>
|
||||
<p class="submit-buttons">
|
||||
<input class="button1" type="submit" id="submit" name="submit" value="{{ lang('SUBMIT') }}" />
|
||||
<input class="button2" type="reset" id="reset" name="reset" value="{{ lang('RESET') }}" />
|
||||
</p>
|
||||
<input type="hidden" name="select_captcha" value="{{ CAPTCHA_NAME }}" />
|
||||
<input type="hidden" name="configure" value="1" />
|
||||
{{ S_FORM_TOKEN }}
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
{% include('overall_footer.html') %}
|
23
phpBB/adm/style/captcha_turnstile_acp_demo.html
Normal file
23
phpBB/adm/style/captcha_turnstile_acp_demo.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<dl>
|
||||
<dt><div id="captcha_turnstile" data-language="{{ lang('TURNSTILE_LANG') }}"{% if TURNSTILE_THEME %} data-theme="{{ TURNSTILE_THEME }}"{% endif %}></div></dt>
|
||||
</dl>
|
||||
{% INCLUDEJS U_TURNSTILE_SCRIPT %}
|
||||
<script>
|
||||
function domReady(callBack) {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', callBack);
|
||||
} else {
|
||||
callBack();
|
||||
}
|
||||
}
|
||||
|
||||
domReady(() => {
|
||||
/* global turnstile */
|
||||
console.debug('_turnstileCb called');
|
||||
|
||||
turnstile.render('#captcha_turnstile', {
|
||||
sitekey: '1x00000000000000000000AA',
|
||||
theme: 'light',
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -19,7 +19,11 @@ services:
|
|||
shared: false
|
||||
arguments:
|
||||
- '@config'
|
||||
- '@dbal.conn'
|
||||
- '@language'
|
||||
- '@request'
|
||||
- '@template'
|
||||
- '@user'
|
||||
- '%core.root_path%'
|
||||
- '%core.php_ext%'
|
||||
calls:
|
||||
|
@ -54,3 +58,19 @@ services:
|
|||
- ['set_name', ['core.captcha.plugins.recaptcha_v3']]
|
||||
tags:
|
||||
- { name: captcha.plugins }
|
||||
|
||||
core.captcha.plugins.turnstile:
|
||||
class: phpbb\captcha\plugins\turnstile
|
||||
shared: false
|
||||
arguments:
|
||||
- '@config'
|
||||
- '@dbal.conn'
|
||||
- '@language'
|
||||
- '@log'
|
||||
- '@request'
|
||||
- '@template'
|
||||
- '@user'
|
||||
calls:
|
||||
- ['set_name', ['core.captcha.plugins.turnstile']]
|
||||
tags:
|
||||
- { name: captcha.plugins }
|
||||
|
|
|
@ -95,7 +95,7 @@ class acp_captcha
|
|||
add_form_key($form_key);
|
||||
|
||||
$submit = $request->variable('main_submit', false);
|
||||
$error = $cfg_array = array();
|
||||
$errors = $cfg_array = array();
|
||||
|
||||
if ($submit)
|
||||
{
|
||||
|
@ -103,13 +103,13 @@ class acp_captcha
|
|||
{
|
||||
$cfg_array[$config_var] = $request->variable($config_var, $options['default']);
|
||||
}
|
||||
validate_config_vars($config_vars, $cfg_array, $error);
|
||||
validate_config_vars($config_vars, $cfg_array, $errors);
|
||||
|
||||
if (!check_form_key($form_key))
|
||||
{
|
||||
$error[] = $user->lang['FORM_INVALID'];
|
||||
$errors[] = $user->lang['FORM_INVALID'];
|
||||
}
|
||||
if ($error)
|
||||
if ($errors)
|
||||
{
|
||||
$submit = false;
|
||||
}
|
||||
|
@ -128,11 +128,9 @@ class acp_captcha
|
|||
if (isset($captchas['available'][$selected]))
|
||||
{
|
||||
$old_captcha = $factory->get_instance($config['captcha_plugin']);
|
||||
$old_captcha->uninstall();
|
||||
$old_captcha->garbage_collect();
|
||||
|
||||
$config->set('captcha_plugin', $selected);
|
||||
$new_captcha = $factory->get_instance($config['captcha_plugin']);
|
||||
$new_captcha->install();
|
||||
|
||||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_VISUAL');
|
||||
}
|
||||
|
@ -145,17 +143,24 @@ class acp_captcha
|
|||
}
|
||||
else
|
||||
{
|
||||
$captcha_select = '';
|
||||
$captcha_options = [];
|
||||
foreach ($captchas['available'] as $value => $title)
|
||||
{
|
||||
$current = ($selected !== false && $value == $selected) ? ' selected="selected"' : '';
|
||||
$captcha_select .= '<option value="' . $value . '"' . $current . '>' . $user->lang($title) . '</option>';
|
||||
$captcha_options[] = [
|
||||
'value' => $value,
|
||||
'label' => $user->lang($title),
|
||||
'selected' => $selected !== false && $value == $selected,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($captchas['unavailable'] as $value => $title)
|
||||
{
|
||||
$current = ($selected !== false && $value == $selected) ? ' selected="selected"' : '';
|
||||
$captcha_select .= '<option value="' . $value . '"' . $current . ' class="disabled-option">' . $user->lang($title) . '</option>';
|
||||
$captcha_options[] = [
|
||||
'value' => $value,
|
||||
'label' => $user->lang($title),
|
||||
'selected' => $selected !== false && $value == $selected,
|
||||
'class' => 'disabled-option',
|
||||
];
|
||||
}
|
||||
|
||||
$demo_captcha = $factory->get_instance($selected);
|
||||
|
@ -168,8 +173,12 @@ class acp_captcha
|
|||
$template->assign_vars(array(
|
||||
'CAPTCHA_PREVIEW_TPL' => $demo_captcha->get_demo_template($id),
|
||||
'S_CAPTCHA_HAS_CONFIG' => $demo_captcha->has_config(),
|
||||
'CAPTCHA_SELECT' => $captcha_select,
|
||||
'ERROR_MSG' => implode('<br />', $error),
|
||||
'CAPTCHA_SELECT' => [
|
||||
'tag' => 'select',
|
||||
'name' => 'select_captcha',
|
||||
'options' => $captcha_options,
|
||||
],
|
||||
'ERRORS' => $errors,
|
||||
|
||||
'U_ACTION' => $this->u_action,
|
||||
));
|
||||
|
|
|
@ -152,9 +152,13 @@ define('FULL_FOLDER_DELETE', -2);
|
|||
define('FULL_FOLDER_HOLD', -1);
|
||||
|
||||
// Confirm types
|
||||
/** @deprecated 4.0.0-a1 Replaced by \phpbb\captcha\plugins\confirm_type::REGISTRATION, to be removed in 5.0.0-a1 */
|
||||
define('CONFIRM_REG', 1);
|
||||
/** @deprecated 4.0.0-a1 Replaced by \phpbb\captcha\plugins\confirm_type::LOGIN, to be removed in 5.0.0-a1 */
|
||||
define('CONFIRM_LOGIN', 2);
|
||||
/** @deprecated 4.0.0-a1 Replaced by \phpbb\captcha\plugins\confirm_type::POST, to be removed in 5.0.0-a1 */
|
||||
define('CONFIRM_POST', 3);
|
||||
/** @deprecated 4.0.0-a1 Replaced by \phpbb\captcha\plugins\confirm_type::REPORT, to be removed in 5.0.0-a1 */
|
||||
define('CONFIRM_REPORT', 4);
|
||||
|
||||
// Categories - Attachments
|
||||
|
|
|
@ -203,8 +203,8 @@ function adm_page_footer($copyright_html = true)
|
|||
*/
|
||||
function adm_back_link($u_action)
|
||||
{
|
||||
global $user;
|
||||
return '<br /><br /><a href="' . $u_action . '">« ' . $user->lang['BACK_TO_PREV'] . '</a>';
|
||||
global $language;
|
||||
return '<br /><br /><a href="' . $u_action . '">« ' . $language->lang('BACK_TO_PREV') . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -235,8 +235,10 @@ class ucp_register
|
|||
// The CAPTCHA kicks in here. We can't help that the information gets lost on language change.
|
||||
if ($config['enable_confirm'])
|
||||
{
|
||||
$captcha = $phpbb_container->get('captcha.factory')->get_instance($config['captcha_plugin']);
|
||||
$captcha->init(CONFIRM_REG);
|
||||
/** @var \phpbb\captcha\factory $captcha_factory */
|
||||
$captcha_factory = $phpbb_container->get('captcha.factory');
|
||||
$captcha = $captcha_factory->get_instance($config['captcha_plugin']);
|
||||
$captcha->init(\phpbb\captcha\plugins\confirm_type::REGISTRATION);
|
||||
}
|
||||
|
||||
$timezone = $config['board_timezone'];
|
||||
|
@ -291,10 +293,9 @@ class ucp_register
|
|||
|
||||
if ($config['enable_confirm'])
|
||||
{
|
||||
$vc_response = $captcha->validate($data);
|
||||
if ($vc_response !== false)
|
||||
if ($captcha->validate() !== true)
|
||||
{
|
||||
$error[] = $vc_response;
|
||||
$error[] = $captcha->get_error();
|
||||
}
|
||||
|
||||
if ($config['max_reg_attempts'] && $captcha->get_attempt_count() > $config['max_reg_attempts'])
|
||||
|
@ -426,7 +427,7 @@ class ucp_register
|
|||
}
|
||||
|
||||
// Okay, captcha, your job is done.
|
||||
if ($config['enable_confirm'] && isset($captcha))
|
||||
if ($config['enable_confirm'])
|
||||
{
|
||||
$captcha->reset();
|
||||
}
|
||||
|
|
|
@ -77,6 +77,9 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd_wave',
|
|||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd_x_grid', '25');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd_y_grid', '25');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_plugin', 'core.captcha.plugins.incomplete');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_turnstile_sitekey', '');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_turnstile_secret', '');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_turnstile_theme', 'light');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('check_attachment_content', '1');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('check_dnsbl', '0');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('chg_passforce', '0');
|
||||
|
|
|
@ -61,4 +61,5 @@ $lang = array_merge($lang, array(
|
|||
|
||||
'QA_ERROR_MSG' => 'Please fill in all fields and enter at least one answer.',
|
||||
'QA_LAST_QUESTION' => 'You cannot delete all questions while the plugin is active.',
|
||||
'QA_NO_QUESTIONS' => 'There are no questions yet.',
|
||||
));
|
||||
|
|
53
phpBB/language/en/captcha_turnstile.php
Normal file
53
phpBB/language/en/captcha_turnstile.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?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.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* DO NOT CHANGE
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($lang) || !is_array($lang))
|
||||
{
|
||||
$lang = [];
|
||||
}
|
||||
|
||||
// DEVELOPERS PLEASE NOTE
|
||||
//
|
||||
// All language files should use UTF-8 as their encoding and the files must not contain a BOM.
|
||||
//
|
||||
// Placeholders can now contain order information, e.g. instead of
|
||||
// 'Page %s of %s' you can (and should) write 'Page %1$s of %2$s', this allows
|
||||
// translators to re-order the output of data while ensuring it remains correct
|
||||
//
|
||||
// You do not need this where single placeholders are used, e.g. 'Message %d' is fine
|
||||
// equally where a string contains only two placeholders which are used to wrap text
|
||||
// in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine
|
||||
|
||||
$lang = array_merge($lang, [
|
||||
'CAPTCHA_TURNSTILE' => 'Turnstile',
|
||||
'CAPTCHA_TURNSTILE_INCORRECT' => 'The solution you provided was incorrect',
|
||||
'CAPTCHA_TURNSTILE_NOSCRIPT' => 'Please enable JavaScript in your browser to load the challenge.',
|
||||
'CAPTCHA_TURNSTILE_NOT_AVAILABLE' => 'In order to use Turnstile you must create a <a href="https://www.cloudflare.com/products/turnstile/">Cloudflare account</a>.',
|
||||
'CAPTCHA_TURNSTILE_SECRET' => 'Secret key',
|
||||
'CAPTCHA_TURNSTILE_SECRET_EXPLAIN' => 'Your Turnstile secret key. The secret key can be retrieved from your <a href="https://dash.cloudflare.com/?to=/:account/turnstile">Cloudflare dashboard</a>.',
|
||||
'CAPTCHA_TURNSTILE_SITEKEY' => 'Sitekey',
|
||||
'CAPTCHA_TURNSTILE_SITEKEY_EXPLAIN' => 'Your Turnstile sitekey. The sitekey can be retrieved from your <a href="https://dash.cloudflare.com/?to=/:account/turnstile">Cloudflare dashboard</a>.',
|
||||
'CAPTCHA_TURNSTILE_THEME' => 'Widget theme',
|
||||
'CAPTCHA_TURNSTILE_THEME_EXPLAIN' => 'The theme of the CAPTCHA widget. By default, <samp>light</samp> will be used. Other possibilities are <samp>dark</samp> and <samp>auto</samp>, which respects the user’s preference.',
|
||||
'CAPTCHA_TURNSTILE_THEME_AUTO' => 'Auto',
|
||||
'CAPTCHA_TURNSTILE_THEME_DARK' => 'Dark',
|
||||
'CAPTCHA_TURNSTILE_THEME_LIGHT' => 'Light',
|
||||
]);
|
|
@ -26,6 +26,7 @@
|
|||
"direction": "ltr",
|
||||
"user-lang": "en-gb",
|
||||
"plural-rule": 1,
|
||||
"recaptcha-lang": "en-GB"
|
||||
"recaptcha-lang": "en-GB",
|
||||
"turnstile-lang": "en"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
256
phpBB/phpbb/captcha/plugins/base.php
Normal file
256
phpBB/phpbb/captcha/plugins/base.php
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
25
phpBB/phpbb/captcha/plugins/confirm_type.php
Normal file
25
phpBB/phpbb/captcha/plugins/confirm_type.php
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
221
phpBB/phpbb/captcha/plugins/legacy_wrapper.php
Normal file
221
phpBB/phpbb/captcha/plugins/legacy_wrapper.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
126
phpBB/phpbb/captcha/plugins/plugin_interface.php
Normal file
126
phpBB/phpbb/captcha/plugins/plugin_interface.php
Normal 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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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())
|
||||
|
|
287
phpBB/phpbb/captcha/plugins/turnstile.php
Normal file
287
phpBB/phpbb/captcha/plugins/turnstile.php
Normal 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,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
89
phpBB/phpbb/db/migration/data/v400/qa_captcha.php
Normal file
89
phpBB/phpbb/db/migration/data/v400/qa_captcha.php
Normal 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']
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
51
phpBB/phpbb/db/migration/data/v400/turnstile_captcha.php
Normal file
51
phpBB/phpbb/db/migration/data/v400/turnstile_captcha.php
Normal 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']],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -120,6 +120,7 @@ class language_file_helper
|
|||
'user_lang' => $data['extra']['user-lang'],
|
||||
'plural_rule' => $data['extra']['plural-rule'],
|
||||
'recaptcha_lang' => $data['extra']['recaptcha-lang'],
|
||||
'turnstile_lang' => $data['extra']['turnstile-lang'] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -455,8 +455,10 @@ if (!$is_authed || !empty($error))
|
|||
|
||||
if ($config['enable_post_confirm'] && !$user->data['is_registered'])
|
||||
{
|
||||
$captcha = $phpbb_container->get('captcha.factory')->get_instance($config['captcha_plugin']);
|
||||
$captcha->init(CONFIRM_POST);
|
||||
/** @var \phpbb\captcha\factory $captcha_factory */
|
||||
$captcha_factory = $phpbb_container->get('captcha.factory');
|
||||
$captcha = $captcha_factory->get_instance($config['captcha_plugin']);
|
||||
$captcha->init(\phpbb\captcha\plugins\confirm_type::POST);
|
||||
}
|
||||
|
||||
// Is the user able to post within this forum?
|
||||
|
@ -1208,15 +1210,9 @@ if ($submit || $preview || $refresh)
|
|||
|
||||
if ($config['enable_post_confirm'] && !$user->data['is_registered'] && in_array($mode, array('quote', 'post', 'reply')))
|
||||
{
|
||||
$captcha_data = array(
|
||||
'message' => $request->variable('message', '', true),
|
||||
'subject' => $request->variable('subject', '', true),
|
||||
'username' => $request->variable('username', '', true),
|
||||
);
|
||||
$vc_response = $captcha->validate($captcha_data);
|
||||
if ($vc_response)
|
||||
if ($captcha->validate() !== true)
|
||||
{
|
||||
$error[] = $vc_response;
|
||||
$error[] = $captcha->get_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1600,7 +1596,7 @@ if ($submit || $preview || $refresh)
|
|||
);
|
||||
extract($phpbb_dispatcher->trigger_event('core.posting_modify_submit_post_after', compact($vars)));
|
||||
|
||||
if ($config['enable_post_confirm'] && !$user->data['is_registered'] && (isset($captcha) && $captcha->is_solved() === true) && ($mode == 'post' || $mode == 'reply' || $mode == 'quote'))
|
||||
if ($config['enable_post_confirm'] && !$user->data['is_registered'] && $captcha->is_solved() === true && ($mode == 'post' || $mode == 'reply' || $mode == 'quote'))
|
||||
{
|
||||
$captcha->reset();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
name="{{ NAME }}"
|
||||
{% if TOGGLEABLE %}data-togglable-settings="true"{% endif %}
|
||||
{% if MULTIPLE %}multiple="multiple"{% endif %}
|
||||
{% if ONCHANGE %}onchange="{{ ONCHANGE }}"{% endif %}
|
||||
{% if SIZE %}size="{{ SIZE }}"{% endif %}>
|
||||
{% for element in OPTIONS %}
|
||||
{% if not GROUP_ONLY and element.options %}
|
||||
|
|
23
phpBB/styles/prosilver/template/captcha_turnstile.html
Normal file
23
phpBB/styles/prosilver/template/captcha_turnstile.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% if CONFIRM_TYPE_REGISTRATION %}
|
||||
<div class="panel captcha-panel">
|
||||
<div class="inner">
|
||||
<h3 class="captcha-title">{{ lang('CONFIRMATION') }}</h3>
|
||||
<fieldset class="fields2">
|
||||
{% endif %}
|
||||
{% if S_TURNSTILE_AVAILABLE %}
|
||||
<noscript>
|
||||
<div>{{ lang('CAPTCHA_TURNSTILE_NOSCRIPT') }}</div>
|
||||
</noscript>
|
||||
|
||||
<script src="{{ U_TURNSTILE_SCRIPT }}" async defer></script>
|
||||
|
||||
{# The cf-turnstile class is used in JavaScript #}
|
||||
<div class="cf-turnstile" data-language="{{ lang('TURNSTILE_LANG') }}" data-sitekey="{{ TURNSTILE_SITEKEY }}"{% if TURNSTILE_THEME %} data-theme="{{ TURNSTILE_THEME }}"{% endif %}></div>
|
||||
{% else %}
|
||||
{{ lang('CAPTCHA_TURNSTILE_NOT_AVAILABLE') }}
|
||||
{% endif %}
|
||||
{% if CONFIRM_TYPE_REGISTRATION %}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -11,11 +11,15 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use phpbb\captcha\plugins\confirm_type;
|
||||
use phpbb\captcha\plugins\incomplete;
|
||||
use phpbb\config\config;
|
||||
use phpbb\language\language;
|
||||
use phpbb\request\request;
|
||||
use phpbb\template\template;
|
||||
use phpbb\user;
|
||||
|
||||
class phpbb_captcha_incomplete_test extends phpbb_test_case
|
||||
class phpbb_captcha_incomplete_test extends phpbb_database_test_case
|
||||
{
|
||||
protected config $config;
|
||||
|
||||
|
@ -32,21 +36,34 @@ class phpbb_captcha_incomplete_test extends phpbb_test_case
|
|||
$this->assigned_vars = array_merge($this->assigned_vars, $vars);
|
||||
}
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->createXMLDataSet(__DIR__ . '/../fixtures/empty.xml');
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
global $phpbb_root_path, $phpEx;
|
||||
|
||||
$this->config = new config([]);
|
||||
$this->template = $this->getMockBuilder('\phpbb\template\twig\twig')
|
||||
->setMethods(['assign_vars'])
|
||||
->onlyMethods(['assign_vars'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->template->method('assign_vars')
|
||||
->willReturnCallback([$this, 'assign_vars']);
|
||||
$db = $this->new_dbal();
|
||||
$language = $this->createMock(language::class);
|
||||
$request = $this->createMock(request::class);
|
||||
$user = $this->createMock(user::class);
|
||||
|
||||
$this->incomplete_captcha = new incomplete(
|
||||
$this->config,
|
||||
$db,
|
||||
$language,
|
||||
$request,
|
||||
$this->template,
|
||||
$user,
|
||||
$phpbb_root_path,
|
||||
$phpEx
|
||||
);
|
||||
|
@ -57,29 +74,25 @@ class phpbb_captcha_incomplete_test extends phpbb_test_case
|
|||
$this->assertTrue($this->incomplete_captcha->is_available());
|
||||
$this->assertFalse($this->incomplete_captcha->is_solved());
|
||||
$this->assertFalse($this->incomplete_captcha->validate());
|
||||
$this->assertSame('CAPTCHA_INCOMPLETE', incomplete::get_name());
|
||||
$this->incomplete_captcha->init(0);
|
||||
$this->incomplete_captcha->execute();
|
||||
$this->incomplete_captcha->execute_demo();
|
||||
$this->assertFalse($this->incomplete_captcha->has_config());
|
||||
$this->incomplete_captcha->set_name('foo');
|
||||
$this->assertSame('CAPTCHA_INCOMPLETE', $this->incomplete_captcha->get_name());
|
||||
$this->incomplete_captcha->init(confirm_type::UNDEFINED);
|
||||
$this->assertEmpty($this->assigned_vars);
|
||||
$this->assertEmpty($this->incomplete_captcha->get_demo_template(0));
|
||||
}
|
||||
|
||||
public function test_get_generator_class(): void
|
||||
{
|
||||
$this->expectException(\phpbb\exception\runtime_exception::class);
|
||||
$this->incomplete_captcha->get_generator_class();
|
||||
$this->assertEmpty($this->incomplete_captcha->get_error());
|
||||
$this->assertSame(0, $this->incomplete_captcha->get_attempt_count());
|
||||
}
|
||||
|
||||
public function test_get_tempate(): void
|
||||
{
|
||||
$this->incomplete_captcha->init(CONFIRM_REG);
|
||||
$this->incomplete_captcha->init(confirm_type::REGISTRATION);
|
||||
$this->assertSame('captcha_incomplete.html', $this->incomplete_captcha->get_template());
|
||||
$this->assertEquals('CONFIRM_INCOMPLETE', $this->assigned_vars['CONFIRM_LANG']);
|
||||
|
||||
$this->assigned_vars = [];
|
||||
|
||||
$this->incomplete_captcha->init(CONFIRM_POST);
|
||||
$this->incomplete_captcha->init(confirm_type::POST);
|
||||
$this->assertSame('captcha_incomplete.html', $this->incomplete_captcha->get_template());
|
||||
$this->assertEquals('CONFIRM_INCOMPLETE', $this->assigned_vars['CONFIRM_LANG']);
|
||||
}
|
||||
|
|
261
tests/captcha/legacy_wrapper_test.php
Normal file
261
tests/captcha/legacy_wrapper_test.php
Normal file
|
@ -0,0 +1,261 @@
|
|||
<?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.
|
||||
*
|
||||
*/
|
||||
|
||||
use phpbb\captcha\plugins\confirm_type;
|
||||
use phpbb\captcha\plugins\legacy_wrapper;
|
||||
|
||||
class phpbb_captcha_legacy_wrapper_test extends phpbb_test_case
|
||||
{
|
||||
private $legacy_captcha;
|
||||
private $legacy_wrapper;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->legacy_captcha = $this->createMock(stdClass::class);
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
}
|
||||
|
||||
public function test_is_available_with_method_exists(): void
|
||||
{
|
||||
// Simulate is_available method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['is_available'])
|
||||
->getMock();
|
||||
$this->legacy_captcha->method('is_available')->willReturn(true);
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
|
||||
$this->assertTrue($this->legacy_wrapper->is_available());
|
||||
}
|
||||
|
||||
public function test_is_available_without_method_exists(): void
|
||||
{
|
||||
// Simulate is_available method does not exist in the legacy captcha
|
||||
$this->assertFalse($this->legacy_wrapper->is_available());
|
||||
}
|
||||
|
||||
public function test_has_config_with_method_exists(): void
|
||||
{
|
||||
// Simulate has_config method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['has_config'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('has_config')->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->legacy_wrapper->has_config());
|
||||
}
|
||||
|
||||
public function test_has_config_without_method_exists(): void
|
||||
{
|
||||
// Simulate has_config method does not exist in the legacy captcha
|
||||
$this->assertFalse($this->legacy_wrapper->has_config());
|
||||
}
|
||||
|
||||
public function test_get_name_with_method_exists(): void
|
||||
{
|
||||
// Simulate get_name method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['get_name'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('get_name')->willReturn('LegacyCaptchaName');
|
||||
|
||||
$this->assertSame('LegacyCaptchaName', $this->legacy_wrapper->get_name());
|
||||
}
|
||||
|
||||
public function test_get_name_without_method_exists(): void
|
||||
{
|
||||
// Simulate get_name method does not exist in the legacy captcha
|
||||
$this->assertSame('', $this->legacy_wrapper->get_name());
|
||||
}
|
||||
|
||||
public function test_set_name_with_method_exists(): void
|
||||
{
|
||||
// Simulate set_name method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['set_name'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->expects($this->once())->method('set_name')->with('NewName');
|
||||
|
||||
$this->legacy_wrapper->set_name('NewName');
|
||||
}
|
||||
|
||||
public function test_init_with_method_exists(): void
|
||||
{
|
||||
// Simulate init method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['init'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->expects($this->once())->method('init')->with(confirm_type::REGISTRATION->value);
|
||||
|
||||
$this->legacy_wrapper->init(confirm_type::REGISTRATION);
|
||||
}
|
||||
|
||||
public function test_get_hidden_fields_with_method_exists(): void
|
||||
{
|
||||
// Simulate get_hidden_fields method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['get_hidden_fields'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('get_hidden_fields')->willReturn(['field1' => 'value1']);
|
||||
|
||||
$this->assertSame(['field1' => 'value1'], $this->legacy_wrapper->get_hidden_fields());
|
||||
}
|
||||
|
||||
public function test_get_hidden_fields_without_method_exists(): void
|
||||
{
|
||||
// Simulate get_hidden_fields method does not exist in the legacy captcha
|
||||
$this->assertSame([], $this->legacy_wrapper->get_hidden_fields());
|
||||
}
|
||||
|
||||
public function test_validate_with_error(): void
|
||||
{
|
||||
// Simulate validate method returns an error
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['validate'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('validate')->willReturn('Captcha Error');
|
||||
|
||||
$this->assertFalse($this->legacy_wrapper->validate());
|
||||
$this->assertSame('Captcha Error', $this->legacy_wrapper->get_error());
|
||||
}
|
||||
|
||||
public function test_validate_without_method_exists(): void
|
||||
{
|
||||
$this->assertFalse($this->legacy_wrapper->validate());
|
||||
}
|
||||
|
||||
public function test_validate_without_error(): void
|
||||
{
|
||||
// Simulate validate method does not return an error
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['validate'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('validate')->willReturn(null);
|
||||
|
||||
$this->assertTrue($this->legacy_wrapper->validate());
|
||||
}
|
||||
|
||||
public function test_is_solved_with_method_exists(): void
|
||||
{
|
||||
// Simulate is_solved method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['is_solved'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('is_solved')->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->legacy_wrapper->is_solved());
|
||||
}
|
||||
|
||||
public function test_is_solved_without_method_exists(): void
|
||||
{
|
||||
// Simulate is_solved method does not exist in the legacy captcha
|
||||
$this->assertFalse($this->legacy_wrapper->is_solved());
|
||||
}
|
||||
|
||||
public function test_reset_with_method_exists(): void
|
||||
{
|
||||
// Simulate reset method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['reset'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->expects($this->once())->method('reset');
|
||||
|
||||
$this->legacy_wrapper->reset();
|
||||
}
|
||||
|
||||
public function test_get_attempt_count_with_method_exists(): void
|
||||
{
|
||||
// Simulate get_attempt_count method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['get_attempt_count'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('get_attempt_count')->willReturn(5);
|
||||
|
||||
$this->assertSame(5, $this->legacy_wrapper->get_attempt_count());
|
||||
}
|
||||
|
||||
public function test_get_attempt_count_without_method_exists(): void
|
||||
{
|
||||
// Simulate get_attempt_count method does not exist in the legacy captcha
|
||||
$this->assertSame(PHP_INT_MAX, $this->legacy_wrapper->get_attempt_count());
|
||||
}
|
||||
|
||||
public function test_get_template_with_method_exists(): void
|
||||
{
|
||||
// Simulate get_template method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['get_template'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('get_template')->willReturn('template_content');
|
||||
|
||||
$this->assertSame('template_content', $this->legacy_wrapper->get_template());
|
||||
}
|
||||
|
||||
public function test_get_template_without_method_exists(): void
|
||||
{
|
||||
// Simulate get_template method does not exist in the legacy captcha
|
||||
$this->assertSame('', $this->legacy_wrapper->get_template());
|
||||
}
|
||||
|
||||
public function test_get_demo_template_with_method_exists(): void
|
||||
{
|
||||
// Simulate get_demo_template method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['get_demo_template'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->method('get_demo_template')->willReturn('demo_template_content');
|
||||
|
||||
$this->assertSame('demo_template_content', $this->legacy_wrapper->get_demo_template());
|
||||
}
|
||||
|
||||
public function test_get_demo_template_without_method_exists(): void
|
||||
{
|
||||
// Simulate get_demo_template method does not exist in the legacy captcha
|
||||
$this->assertSame('', $this->legacy_wrapper->get_demo_template());
|
||||
}
|
||||
|
||||
public function test_garbage_collect_with_method_exists(): void
|
||||
{
|
||||
// Simulate garbage_collect method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['garbage_collect'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->expects($this->once())->method('garbage_collect')->with(confirm_type::REGISTRATION->value);
|
||||
|
||||
$this->legacy_wrapper->garbage_collect(confirm_type::REGISTRATION);
|
||||
}
|
||||
|
||||
public function test_acp_page_with_method_exists(): void
|
||||
{
|
||||
// Simulate acp_page method exists in the legacy captcha
|
||||
$this->legacy_captcha = $this->getMockBuilder(stdClass::class)
|
||||
->addMethods(['acp_page'])
|
||||
->getMock();
|
||||
$this->legacy_wrapper = new legacy_wrapper($this->legacy_captcha);
|
||||
$this->legacy_captcha->expects($this->once())->method('acp_page')->with(1, 'module');
|
||||
|
||||
$this->legacy_wrapper->acp_page(1, 'module');
|
||||
}
|
||||
}
|
|
@ -41,10 +41,6 @@ class phpbb_captcha_qa_test extends \phpbb_database_test_case
|
|||
|
||||
public function test_is_installed()
|
||||
{
|
||||
$this->assertFalse($this->qa->is_installed());
|
||||
|
||||
$this->qa->install();
|
||||
|
||||
$this->assertTrue($this->qa->is_installed());
|
||||
}
|
||||
|
||||
|
|
547
tests/captcha/turnstile_test.php
Normal file
547
tests/captcha/turnstile_test.php
Normal file
|
@ -0,0 +1,547 @@
|
|||
<?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.
|
||||
*
|
||||
*/
|
||||
|
||||
use phpbb\captcha\plugins\confirm_type;
|
||||
use phpbb\captcha\plugins\turnstile;
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\form\form_helper;
|
||||
use phpbb\language\language;
|
||||
use phpbb\log\log_interface;
|
||||
use phpbb\request\request;
|
||||
use phpbb\request\request_interface;
|
||||
use phpbb\template\template;
|
||||
use phpbb\user;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
|
||||
require_once __DIR__ . '/../../phpBB/includes/functions_acp.php';
|
||||
|
||||
class phpbb_captcha_turnstile_test extends \phpbb_database_test_case
|
||||
{
|
||||
/** @var turnstile */
|
||||
protected $turnstile;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $config;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $db;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $language;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $log;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $request;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $template;
|
||||
|
||||
/** @var PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $user;
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->createXMLDataSet(__DIR__ . '/../fixtures/empty.xml');
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Mock the dependencies
|
||||
$this->config = $this->createMock(config::class);
|
||||
$this->db = $this->new_dbal();
|
||||
$this->language = $this->createMock(language::class);
|
||||
$this->log = $this->createMock(log_interface::class);
|
||||
$this->request = $this->createMock(request::class);
|
||||
$this->template = $this->createMock(template::class);
|
||||
$this->user = $this->createMock(user::class);
|
||||
|
||||
$this->language->method('lang')->willReturnArgument(0);
|
||||
|
||||
// Instantiate the turnstile class with the mocked dependencies
|
||||
$this->turnstile = new turnstile(
|
||||
$this->config,
|
||||
$this->db,
|
||||
$this->language,
|
||||
$this->log,
|
||||
$this->request,
|
||||
$this->template,
|
||||
$this->user
|
||||
);
|
||||
}
|
||||
|
||||
public function test_is_available(): void
|
||||
{
|
||||
// Test when both sitekey and secret are present
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_secret', 'secret_value'],
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, 'confirm_id'],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->assertTrue($this->turnstile->is_available());
|
||||
|
||||
$this->assertEquals(0, $this->turnstile->get_attempt_count());
|
||||
}
|
||||
|
||||
public function test_attempt_count_increase(): void
|
||||
{
|
||||
// Test when both sitekey and secret are present
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_secret', 'secret_value'],
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, 'confirm_id'],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->turnstile->init(confirm_type::REGISTRATION);
|
||||
$this->assertFalse($this->turnstile->validate());
|
||||
|
||||
$confirm_id_reflection = new \ReflectionProperty($this->turnstile, 'confirm_id');
|
||||
$confirm_id = $confirm_id_reflection->getValue($this->turnstile);
|
||||
|
||||
$this->request = $this->createMock(request::class);
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, $confirm_id],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->turnstile = new turnstile(
|
||||
$this->config,
|
||||
$this->db,
|
||||
$this->language,
|
||||
$this->log,
|
||||
$this->request,
|
||||
$this->template,
|
||||
$this->user
|
||||
);
|
||||
|
||||
$this->turnstile->init(confirm_type::REGISTRATION);
|
||||
$this->assertEquals(1, $this->turnstile->get_attempt_count());
|
||||
|
||||
// Run some garbage collection
|
||||
$this->turnstile->garbage_collect(confirm_type::REGISTRATION);
|
||||
|
||||
// Start again at 0 after garbage collection
|
||||
$this->turnstile->init(confirm_type::REGISTRATION);
|
||||
$this->assertEquals(0, $this->turnstile->get_attempt_count());
|
||||
}
|
||||
|
||||
public function test_reset(): void
|
||||
{
|
||||
// Test when both sitekey and secret are present
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_secret', 'secret_value'],
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, 'confirm_id'],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->turnstile->init(confirm_type::REGISTRATION);
|
||||
$this->assertFalse($this->turnstile->validate());
|
||||
$this->turnstile->reset();
|
||||
|
||||
$confirm_id_reflection = new \ReflectionProperty($this->turnstile, 'confirm_id');
|
||||
$confirm_id = $confirm_id_reflection->getValue($this->turnstile);
|
||||
|
||||
$this->request = $this->createMock(request::class);
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, $confirm_id],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->turnstile = new turnstile(
|
||||
$this->config,
|
||||
$this->db,
|
||||
$this->language,
|
||||
$this->log,
|
||||
$this->request,
|
||||
$this->template,
|
||||
$this->user
|
||||
);
|
||||
|
||||
$this->turnstile->init(confirm_type::REGISTRATION);
|
||||
// Should be zero attempts since we reset the captcha
|
||||
$this->assertEquals(0, $this->turnstile->get_attempt_count());
|
||||
}
|
||||
|
||||
public function test_get_hidden_fields(): void
|
||||
{
|
||||
// Test when both sitekey and secret are present
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_secret', 'secret_value'],
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, 'confirm_id'],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->turnstile->init(confirm_type::REGISTRATION);
|
||||
$this->assertFalse($this->turnstile->validate());
|
||||
$this->turnstile->reset();
|
||||
|
||||
$confirm_id_reflection = new \ReflectionProperty($this->turnstile, 'confirm_id');
|
||||
$confirm_id = $confirm_id_reflection->getValue($this->turnstile);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'confirm_id' => $confirm_id,
|
||||
'confirm_code' => '',
|
||||
],
|
||||
$this->turnstile->get_hidden_fields(),
|
||||
);
|
||||
$this->assertEquals('CONFIRM_CODE_WRONG', $this->turnstile->get_error());
|
||||
}
|
||||
|
||||
public function test_not_available(): void
|
||||
{
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, 'confirm_id'],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
// Test when sitekey or secret is missing
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', ''],
|
||||
['captcha_turnstile_secret', 'secret_value'],
|
||||
]);
|
||||
|
||||
$this->assertFalse($this->turnstile->is_available());
|
||||
}
|
||||
|
||||
public function test_get_name(): void
|
||||
{
|
||||
$this->assertEquals('CAPTCHA_TURNSTILE', $this->turnstile->get_name());
|
||||
}
|
||||
|
||||
public function test_set_Name(): void
|
||||
{
|
||||
$this->turnstile->set_name('custom_service');
|
||||
$service_name_property = new \ReflectionProperty($this->turnstile, 'service_name');
|
||||
$service_name_property->setAccessible(true);
|
||||
$this->assertEquals('custom_service', $service_name_property->getValue($this->turnstile));
|
||||
}
|
||||
|
||||
public function test_validate_without_response(): void
|
||||
{
|
||||
// Test when there is no Turnstile response
|
||||
$this->request->method('variable')->with('cf-turnstile-response')->willReturn('');
|
||||
|
||||
$this->assertFalse($this->turnstile->validate());
|
||||
}
|
||||
|
||||
public function test_validate_with_response_success(): void
|
||||
{
|
||||
// Mock the request and response from the Turnstile API
|
||||
$this->request->method('variable')->with('cf-turnstile-response')->willReturn('valid_response');
|
||||
$this->request->method('header')->with('CF-Connecting-IP')->willReturn('127.0.0.1');
|
||||
|
||||
// Mock the GuzzleHttp client and response
|
||||
$client_mock = $this->createMock(Client::class);
|
||||
$response_mock = $this->createMock(Response::class);
|
||||
|
||||
$client_mock->method('request')->willReturn($response_mock);
|
||||
$response_mock->method('getBody')->willReturn(json_encode(['success' => true]));
|
||||
|
||||
// Mock config values for secret
|
||||
$this->config->method('offsetGet')->willReturn('secret_value');
|
||||
|
||||
// Use reflection to inject the mocked client into the turnstile class
|
||||
$reflection = new \ReflectionClass($this->turnstile);
|
||||
$client_property = $reflection->getProperty('client');
|
||||
$client_property->setAccessible(true);
|
||||
$client_property->setValue($this->turnstile, $client_mock);
|
||||
|
||||
// Validate that the CAPTCHA was solved successfully
|
||||
$this->assertTrue($this->turnstile->validate());
|
||||
}
|
||||
|
||||
public function test_validate_with_guzzle_exception(): void
|
||||
{
|
||||
// Mock the request and response from the Turnstile API
|
||||
$this->request->method('variable')->with('cf-turnstile-response')->willReturn('valid_response');
|
||||
$this->request->method('header')->with('CF-Connecting-IP')->willReturn('127.0.0.1');
|
||||
|
||||
// Mock the GuzzleHttp client and response
|
||||
$client_mock = $this->createMock(Client::class);
|
||||
|
||||
$request_mock = $this->createMock(\GuzzleHttp\Psr7\Request::class);
|
||||
$exception = new \GuzzleHttp\Exception\ConnectException('Failed at connecting', $request_mock);
|
||||
$client_mock->method('request')->willThrowException($exception);
|
||||
|
||||
// Mock config values for secret
|
||||
$this->config->method('offsetGet')->willReturn('secret_value');
|
||||
|
||||
// Use reflection to inject the mocked client into the turnstile class
|
||||
$reflection = new \ReflectionClass($this->turnstile);
|
||||
$client_property = $reflection->getProperty('client');
|
||||
$client_property->setAccessible(true);
|
||||
$client_property->setValue($this->turnstile, $client_mock);
|
||||
|
||||
// Validatation fails due to guzzle exception
|
||||
$this->assertFalse($this->turnstile->validate());
|
||||
}
|
||||
|
||||
public function test_validate_previous_solve(): void
|
||||
{
|
||||
// Use reflection to inject the mocked client into the turnstile class
|
||||
$reflection = new \ReflectionClass($this->turnstile);
|
||||
$confirm_id = $reflection->getProperty('confirm_id');
|
||||
$confirm_id->setValue($this->turnstile, 'confirm_id');
|
||||
$code_property = $reflection->getProperty('code');
|
||||
$code_property->setValue($this->turnstile, 'test_code');
|
||||
$confirm_code_property = $reflection->getProperty('confirm_code');
|
||||
$confirm_code_property->setValue($this->turnstile, 'test_code');
|
||||
|
||||
// Validate that the CAPTCHA was solved successfully
|
||||
$this->assertTrue($this->turnstile->validate());
|
||||
$this->assertTrue($this->turnstile->is_solved());
|
||||
}
|
||||
|
||||
public function test_has_config(): void
|
||||
{
|
||||
$this->assertTrue($this->turnstile->has_config());
|
||||
}
|
||||
|
||||
public function test_get_client(): void
|
||||
{
|
||||
$turnstile_reflection = new \ReflectionClass($this->turnstile);
|
||||
$get_client_method = $turnstile_reflection->getMethod('get_client');
|
||||
$get_client_method->setAccessible(true);
|
||||
$client_property = $turnstile_reflection->getProperty('client');
|
||||
$client_property->setAccessible(true);
|
||||
|
||||
$this->assertFalse($client_property->isInitialized($this->turnstile));
|
||||
$client = $get_client_method->invoke($this->turnstile);
|
||||
$this->assertNotNull($client);
|
||||
$this->assertInstanceOf(\GuzzleHttp\Client::class, $client);
|
||||
$this->assertTrue($client === $get_client_method->invoke($this->turnstile));
|
||||
}
|
||||
|
||||
public function test_validate_with_response_failure(): void
|
||||
{
|
||||
// Mock the request and response from the Turnstile API
|
||||
$this->request->method('variable')->with('cf-turnstile-response')->willReturn('valid_response');
|
||||
$this->request->method('header')->with('CF-Connecting-IP')->willReturn('127.0.0.1');
|
||||
|
||||
// Mock the GuzzleHttp client and response
|
||||
$client_mock = $this->createMock(Client::class);
|
||||
$response_mock = $this->createMock(Response::class);
|
||||
|
||||
$client_mock->method('request')->willReturn($response_mock);
|
||||
$response_mock->method('getBody')->willReturn(json_encode(['success' => false]));
|
||||
|
||||
// Mock config values for secret
|
||||
$this->config->method('offsetGet')->willReturn('secret_value');
|
||||
|
||||
// Use reflection to inject the mocked client into the turnstile class
|
||||
$reflection = new \ReflectionClass($this->turnstile);
|
||||
$client_property = $reflection->getProperty('client');
|
||||
$client_property->setAccessible(true);
|
||||
$client_property->setValue($this->turnstile, $client_mock);
|
||||
|
||||
// Validate that the CAPTCHA was not solved
|
||||
$this->assertFalse($this->turnstile->validate());
|
||||
}
|
||||
|
||||
public function test_get_template(): void
|
||||
{
|
||||
// Mock is_solved to return false
|
||||
$is_solved_property = new \ReflectionProperty($this->turnstile, 'solved');
|
||||
$is_solved_property->setAccessible(true);
|
||||
$is_solved_property->setValue($this->turnstile, false);
|
||||
|
||||
// Mock the template assignments
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_theme', 'light'],
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['confirm_id', '', false, request_interface::REQUEST, 'confirm_id'],
|
||||
['confirm_code', '', false, request_interface::REQUEST, 'confirm_code']
|
||||
]);
|
||||
|
||||
$this->template->expects($this->once())->method('assign_vars')->with([
|
||||
'S_TURNSTILE_AVAILABLE' => $this->turnstile->is_available(),
|
||||
'TURNSTILE_SITEKEY' => 'sitekey_value',
|
||||
'TURNSTILE_THEME' => 'light',
|
||||
'U_TURNSTILE_SCRIPT' => 'https://challenges.cloudflare.com/turnstile/v0/api.js',
|
||||
'CONFIRM_TYPE_REGISTRATION' => confirm_type::UNDEFINED->value,
|
||||
]);
|
||||
|
||||
$this->assertEquals('captcha_turnstile.html', $this->turnstile->get_template());
|
||||
|
||||
$is_solved_property->setValue($this->turnstile, true);
|
||||
$this->assertEquals('', $this->turnstile->get_template());
|
||||
}
|
||||
|
||||
public function test_get_demo_template(): void
|
||||
{
|
||||
// Mock the config assignments
|
||||
$this->config->method('offsetGet')->willReturn('light');
|
||||
|
||||
$this->template->expects($this->once())->method('assign_vars')->with([
|
||||
'TURNSTILE_THEME' => 'light',
|
||||
'U_TURNSTILE_SCRIPT' => 'https://challenges.cloudflare.com/turnstile/v0/api.js',
|
||||
]);
|
||||
|
||||
$this->assertEquals('captcha_turnstile_acp_demo.html', $this->turnstile->get_demo_template());
|
||||
}
|
||||
|
||||
public function test_acp_page_display(): void
|
||||
{
|
||||
global $phpbb_container, $phpbb_dispatcher, $template;
|
||||
|
||||
$phpbb_container = new phpbb_mock_container_builder();
|
||||
$form_helper = new form_helper($this->config, $this->request, $this->user);
|
||||
$phpbb_container->set('form_helper', $form_helper);
|
||||
$this->user->data['user_id'] = ANONYMOUS;
|
||||
$this->user->data['user_form_salt'] = 'foobar';
|
||||
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
$template = $this->template;
|
||||
|
||||
// Mock the template assignments
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_theme', 'light'],
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturn('');
|
||||
|
||||
$expected = [
|
||||
1 => [
|
||||
'TURNSTILE_THEME' => 'light',
|
||||
'U_TURNSTILE_SCRIPT' => 'https://challenges.cloudflare.com/turnstile/v0/api.js',
|
||||
],
|
||||
2 => [
|
||||
'CAPTCHA_PREVIEW' => 'captcha_turnstile_acp_demo.html',
|
||||
'CAPTCHA_NAME' => '',
|
||||
'CAPTCHA_TURNSTILE_THEME' => 'light',
|
||||
'CAPTCHA_TURNSTILE_THEMES' => ['light', 'dark', 'auto'],
|
||||
'U_ACTION' => 'test_u_action',
|
||||
],
|
||||
];
|
||||
$matcher = $this->exactly(count($expected));
|
||||
$this->template
|
||||
->expects($matcher)
|
||||
->method('assign_vars')
|
||||
->willReturnCallback(function ($template_data) use ($matcher, $expected) {
|
||||
$callNr = $matcher->getInvocationCount();
|
||||
$this->assertEquals($expected[$callNr], $template_data);
|
||||
});
|
||||
|
||||
$module_mock = new ModuleMock();
|
||||
|
||||
$this->turnstile->acp_page('', $module_mock);
|
||||
}
|
||||
|
||||
public function test_acp_page_submit_without_form(): void
|
||||
{
|
||||
global $language, $phpbb_container, $phpbb_dispatcher, $template;
|
||||
|
||||
$language = $this->language;
|
||||
$phpbb_container = new phpbb_mock_container_builder();
|
||||
$form_helper = new form_helper($this->config, $this->request, $this->user);
|
||||
$phpbb_container->set('form_helper', $form_helper);
|
||||
$this->user->data['user_id'] = ANONYMOUS;
|
||||
$this->user->data['user_form_salt'] = 'foobar';
|
||||
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
$template = $this->template;
|
||||
|
||||
// Mock the template assignments
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_theme', 'light'],
|
||||
]);
|
||||
|
||||
$this->request->method('is_set_post')->willReturnMap([
|
||||
['creation_time', ''],
|
||||
['submit', true]
|
||||
]);
|
||||
|
||||
$this->setExpectedTriggerError(E_USER_NOTICE, 'FORM_INVALID');
|
||||
|
||||
$module_mock = new ModuleMock();
|
||||
|
||||
$this->turnstile->acp_page('', $module_mock);
|
||||
}
|
||||
|
||||
public function test_acp_page_submit_valid(): void
|
||||
{
|
||||
global $language, $phpbb_container, $phpbb_dispatcher, $template;
|
||||
|
||||
$language = $this->language;
|
||||
$phpbb_container = new phpbb_mock_container_builder();
|
||||
$form_helper = new form_helper($this->config, $this->request, $this->user);
|
||||
$phpbb_container->set('form_helper', $form_helper);
|
||||
$this->user->data['user_id'] = ANONYMOUS;
|
||||
$this->user->data['user_form_salt'] = 'foobar';
|
||||
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
$template = $this->template;
|
||||
|
||||
$form_tokens = $form_helper->get_form_tokens('acp_captcha');
|
||||
|
||||
// Mock the template assignments
|
||||
$this->config->method('offsetGet')->willReturnMap([
|
||||
['captcha_turnstile_sitekey', 'sitekey_value'],
|
||||
['captcha_turnstile_theme', 'light'],
|
||||
]);
|
||||
$this->config['form_token_lifetime'] = 3600;
|
||||
|
||||
$this->request->method('is_set_post')->willReturnMap([
|
||||
['creation_time', true],
|
||||
['form_token', true],
|
||||
['submit', true]
|
||||
]);
|
||||
|
||||
$this->request->method('variable')->willReturnMap([
|
||||
['creation_time', 0, false, request_interface::REQUEST, $form_tokens['creation_time']],
|
||||
['form_token', '', false, request_interface::REQUEST, $form_tokens['form_token']],
|
||||
['captcha_turnstile_sitekey', '', false, request_interface::REQUEST, 'newsitekey'],
|
||||
['captcha_turnstile_theme', 'light', false, request_interface::REQUEST, 'auto'],
|
||||
]);
|
||||
|
||||
$this->setExpectedTriggerError(E_USER_NOTICE, 'CONFIG_UPDATED');
|
||||
|
||||
$module_mock = new ModuleMock();
|
||||
sleep(1); // sleep for a second to ensure form token validation succeeds
|
||||
|
||||
$this->turnstile->acp_page('', $module_mock);
|
||||
}
|
||||
}
|
||||
|
||||
class ModuleMock
|
||||
{
|
||||
public string $tpl_name = '';
|
||||
public string $page_title = '';
|
||||
public string $u_action = 'test_u_action';
|
||||
}
|
Loading…
Add table
Reference in a new issue