/trunk/TODO |
---|
1,7 → 1,5 |
OIDplus (OP org) |
- in setup : let the user enter a canonical url for the base config ( preset it with the current canonical url ) |
- recaptcha v3 does not work ... implement it |
- recaptcha v2 invisible is not invisible |
TODO Reproduce: |
- microsoft Edge freezing Login with vts challenge captcha, other browsers work fast |
12,6 → 10,8 |
Small things: |
- when you copy something into the clipboard, please show a toast message |
- Show metatag "canonical" only if "canonical url" was set in base config. Don't let this meta tag be filled with automatically detected URLs. |
- Setup: "None" CAPTCHA plugin should be the first option |
Ideas by Simon T.: |
- System status plugin: Check if file owners are mixed |
211,6 → 211,7 |
- How can we make sure that example objects are not exported using oid-info.com export? |
- Administrator-Interface: enable and disable object types |
- detailled change-history of each oid |
- Add a "nonce" to all inline JavaScripts and add this nonce to CSP. Then disallow inline-JavaScripts in CSP completely. |
BUGS? |
- BUG! RA is logged in, then it is deleted => the RA can still edit their OIDs, since their session is not destroyed |
/trunk/doc/config_values.txt |
---|
105,6 → 105,12 |
OIDplus::baseConfig()->setValue('CAPTCHA_PLUGIN', 'None'); |
OIDplus::baseConfig()->setValue('RECAPTCHA_VERSION', OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V2_CHECKBOX); |
Possible values: |
OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V2_CHECKBOX |
OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V2_INVISIBLE |
OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V3 |
OIDplus::baseConfig()->setValue('RECAPTCHA_ENABLED', true); |
Deprecated! |
RECAPTCHA_ENABLED=true becomes CAPTCHA_PLUGIN=ReCAPTCHA |
/trunk/includes/classes/OIDplus.class.php |
---|
348,11 → 348,13 |
if ($db_plugin_name === '') { |
throw new OIDplusConfigInitializationException(_L('No database plugin selected in config file')); |
} |
if (!isset(self::$dbPlugins[$db_plugin_name])) { |
foreach (self::$dbPlugins as $name => $plugin) { |
if (strtolower($name) == strtolower($db_plugin_name)) { |
return $plugin; |
} |
} |
throw new OIDplusConfigInitializationException(_L('Database plugin "%1" not found',$db_plugin_name)); |
} |
return self::$dbPlugins[$db_plugin_name]; |
} |
private static $dbMainSession = null; |
public static function db() { |
397,7 → 399,7 |
if (OIDplus::baseConfig()->getValue('RECAPTCHA_ENABLED', false) && ($captcha_plugin_name === '')) { |
// Legacy config file support! |
$captcha_plugin_name = 'ReCAPTCHA'; |
$captcha_plugin_name = 'reCAPTCHA'; |
} |
if ($captcha_plugin_name === '') $captcha_plugin_name = 'None'; // the "None" plugin is a must-have! |
407,12 → 409,13 |
public static function getActiveCaptchaPlugin() { |
$captcha_plugin_name = OIDplus::getActiveCaptchaPluginId(); |
if (!isset(self::$captchaPlugins[$captcha_plugin_name])) { |
foreach (self::$captchaPlugins as $name => $plugin) { |
if (strtolower($name) == strtolower($captcha_plugin_name)) { |
return $plugin; |
} |
} |
throw new OIDplusConfigInitializationException(_L('CAPTCHA plugin "%1" not found',$captcha_plugin_name)); |
} |
return self::$captchaPlugins[$captcha_plugin_name]; |
} |
# --- Page plugin |
/trunk/includes/classes/OIDplusCaptchaPlugin.class.php |
---|
23,7 → 23,7 |
public abstract static function id(): string; // this is the name that is set to the configuration value OIDplus::baseConfig()->getValue('CAPTCHA_PLUGIN') to identify the CAPTCHA plugin |
public abstract static function isVisible(): bool; |
public abstract function isVisible(): bool; |
public function captchaDomHead() { return ''; } |
/trunk/plugins/viathinksoft/adminPages/050_oobe/oobe.php |
---|
62,7 → 62,7 |
echo '<form method="POST" action="oobe.php">'; |
echo '<input type="hidden" name="sent" value="1">'; |
if (OIDplus::getActiveCaptchaPlugin()::isVisible()) echo '<p><u>'._L('Step %1: Solve CAPTCHA',$step++).'</u></p>'; |
if (OIDplus::getActiveCaptchaPlugin()->isVisible()) echo '<p><u>'._L('Step %1: Solve CAPTCHA',$step++).'</u></p>'; |
if (isset($_REQUEST['sent'])) { |
try { |
OIDplus::getActiveCaptchaPlugin()->captchaVerify($_POST); |
/trunk/plugins/viathinksoft/captcha/hcaptcha/OIDplusCaptchaPluginHCaptcha.class.php |
---|
25,7 → 25,7 |
return 'hCaptcha'; |
} |
public static function isVisible(): bool { |
public function isVisible(): bool { |
return true; |
} |
/trunk/plugins/viathinksoft/captcha/none/OIDplusCaptchaPluginNone.class.php |
---|
25,7 → 25,7 |
return 'None'; |
} |
public static function isVisible(): bool { |
public function isVisible(): bool { |
return false; |
} |
/trunk/plugins/viathinksoft/captcha/recaptcha/OIDplusCaptchaPluginRecaptcha.js |
---|
File deleted |
/trunk/plugins/viathinksoft/captcha/recaptcha/OIDplusCaptchaPluginRecaptcha.class.php |
---|
2,7 → 2,7 |
/* |
* OIDplus 2.0 |
* Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft |
* Copyright 2019 - 2022 Daniel Marschall, ViaThinkSoft |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
21,60 → 21,93 |
class OIDplusCaptchaPluginRecaptcha extends OIDplusCaptchaPlugin { |
/*public*/ const RECAPTCHA_V2_CHECKBOX = 1; |
/*public*/ const RECAPTCHA_V2_INVISIBLE = 2; |
/*public*/ const RECAPTCHA_V3 = 3; |
public static function id(): string { |
return 'ReCAPTCHA'; |
return 'reCAPTCHA'; // TODO: Now it is called "reCAPTCHA" |
} |
public static function isVisible(): bool { |
// TODO: Also implement Google invisible CAPTCHAs |
return true; |
public function isVisible(): bool { |
return OIDplus::baseConfig()->getValue('RECAPTCHA_VERSION', self::RECAPTCHA_V2_CHECKBOX) == self::RECAPTCHA_V2_CHECKBOX; |
} |
public function captchaDomHead() { |
// Here you can add styles and scripts to be included into the HTML <head> part |
return '<script> |
function oidplus_captcha_response() { |
return OIDplusCaptchaPluginRecaptcha.captchaResponse(); |
} |
function oidplus_captcha_reset() { |
return OIDplusCaptchaPluginRecaptcha.captchaReset(); |
} |
</script> |
<script src="https://www.google.com/recaptcha/api.js"></script>'; |
} |
public function captchaGenerate($header_text=null, $footer_text=null) { |
return ($header_text ? '<p>'.$header_text.'</p>' : '') . |
'<noscript>'. |
return '<noscript>'. |
'<p><font color="red">'._L('You need to enable JavaScript to solve the CAPTCHA.').'</font></p>'. |
'</noscript>'. |
'<div id="g-recaptcha" class="g-recaptcha" data-sitekey="'.OIDplus::baseConfig()->getValue('RECAPTCHA_PUBLIC', '').'"></div>'. |
//Don't use jQuery, because we might not have included it (e.g. in oobe.php) |
//'<script> grecaptcha.render($("#g-recaptcha")[0], { "sitekey" : "'.OIDplus::baseConfig()->getValue('RECAPTCHA_PUBLIC', '').'" }); </script>'. |
// TODO: oobe.php:formatted:42 Uncaught TypeError: grecaptcha.render is not a function at oobe.php:formatted:42 (but it still works?!) |
'<script> grecaptcha.render(document.getElementById("g-recaptcha"), { "sitekey" : "'.OIDplus::baseConfig()->getValue('RECAPTCHA_PUBLIC', '').'" }); </script>'. |
($footer_text ? '<p>'.$footer_text.'</p>' : ''); |
(!$this->isVisible() || !$header_text ? '' : '<p>'.$header_text.'</p>'). |
'<div id="recaptcha"></div>'. |
'<input type="hidden" id="oidplus-recaptcha-response" name="oidplus-recaptcha-response">'. |
'<script>'. |
// ' $("form").submit(function(e){'. |
// // TODO: The form must not be submitted before recaptchaFinished() is called! |
// ' event.preventDefault();'. |
// ' });'. |
' var recaptchaLoaded = function() {'. |
' console.log("reCAPTCHA ready");'. |
' grecaptcha.render("recaptcha", {'. |
' "sitekey": "'.OIDplus::baseConfig()->getValue('RECAPTCHA_PUBLIC', '').'",'. |
($this->isVisible() ? '' : |
' "size": "invisible",'). |
' "callback": function (token) {'. // TODO: also 'expired-callback' and 'error-callback' |
' console.log("reCAPTCHA solved");'. |
' document.getElementById("oidplus-recaptcha-response").value = token;'. |
' }'. |
' });'. |
($this->isVisible() ? '' : |
' grecaptcha.execute();'). |
' };'. |
' var oidplus_captcha_response = function() {'. |
' return document.getElementById("oidplus-recaptcha-response").value;'. |
' };'. |
' var oidplus_captcha_reset = function() {'. |
' grecaptcha.reset();'. |
($this->isVisible() ? '' : |
' grecaptcha.execute();'). |
' };'. |
'</script>'. |
'<script src="https://www.google.com/recaptcha/api.js?onload=recaptchaLoaded&render=explicit" async defer></script>'. |
(!$this->isVisible() || !$footer_text ? '' : '<p>'.$footer_text.'</p>'); |
} |
public function captchaVerify($params, $fieldname=null) { |
$secret=OIDplus::baseConfig()->getValue('RECAPTCHA_PRIVATE', ''); |
if (is_null($fieldname)) $fieldname = 'g-recaptcha-response'; // no individual field name (created by oidplus_captcha_response()) means that it is a plain POST event (e.g. by oobe.php) |
if (is_null($fieldname)) $fieldname = 'oidplus-recaptcha-response'; // no individual AJAX field name (created by oidplus_captcha_response()) means that it is a plain POST event (e.g. by oobe.php) |
_CheckParamExists($params, $fieldname); |
$response=$params[$fieldname]; |
$verify=url_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.urlencode($secret).'&response='.urlencode($response)); |
$verify=url_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.urlencode($secret).'&response='.urlencode($response).'&remoteip='.urlencode($_SERVER['REMOTE_ADDR'])); |
if (!$verify) { |
throw new OIDplusException(_L('CAPTCHA not successfully verified')); |
throw new OIDplusException(_L('CAPTCHA not successfully verified').' (Web request failed)'); |
} |
$captcha_success=@json_decode($verify); |
if (!$captcha_success || ($captcha_success->success==false)) { |
throw new OIDplusException(_L('CAPTCHA not successfully verified')); |
$SCORE_THRESHOLD = 0.5; // TODO: Make Score configurable (only V3) |
if (!$captcha_success) { |
throw new OIDplusException(_L('CAPTCHA not successfully verified').' (JSON Decode failed)'); |
} |
if ($captcha_success->success==false) { |
throw new OIDplusException(_L('CAPTCHA not successfully verified').' (Failed)'); |
} |
if (isset($captcha_success->score) && ($captcha_success->score < $SCORE_THRESHOLD)) { |
throw new OIDplusException(_L('CAPTCHA not successfully verified').' (Score '.($captcha_success->score).' too low)'); |
} |
} |
public static function setupHTML(): string { |
return '<div id="CAPTCHAPLUGIN_PARAMS_RECAPTCHA">'. |
'<p>(<a href="https://developers.google.com/recaptcha/intro" target="_blank">'._L('more information and obtain key').'</a>)</p>'. |
'<p>'._L('reCAPTCHA Version').'<br><select id="recaptcha_version">'. |
' <option name="OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V2_CHECKBOX">reCAPTCHA V2 Checkbox</option>'. |
' <option name="OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V2_INVISIBLE">reCAPTCHA V2 Invisible</option>'. |
' <option name="OIDplusCaptchaPluginRecaptcha::RECAPTCHA_V3">reCAPTCHA V3</option>'. |
'</select></p>'. |
'<p>'._L('reCAPTCHA Public key').'<br><input id="recaptcha_public" type="text" onkeypress="rebuild()" onkeyup="rebuild()"> <span id="recaptcha_public_warn"></span></p>'. |
'<p>'._L('reCAPTCHA Private key').'<br><input id="recaptcha_private" type="text" onkeypress="rebuild()" onkeyup="rebuild()"> <span id="recaptcha_private_warn"></span></p>'. |
'</div>'; |
81,7 → 114,6 |
} |
function httpHeaderCheck(&$http_headers) { |
$http_headers["Content-Security-Policy"]["script-src"][] = "https://www.google.com/"; |
$http_headers["Content-Security-Policy"]["script-src"][] = "https://www.gstatic.com/"; |
$http_headers["Content-Security-Policy"]["img-src"][] = "https://www.google.com/"; |
88,7 → 120,6 |
$http_headers["Content-Security-Policy"]["img-src"][] = "https://www.gstatic.com/"; |
$http_headers["Content-Security-Policy"]["frame-src"][] = "https://www.google.com/"; |
$http_headers["Content-Security-Policy"]["frame-src"][] = "https://www.gstatic.com/"; |
} |
} |
/trunk/plugins/viathinksoft/captcha/recaptcha/OIDplusCaptchaPluginRecaptchaSetup.js |
---|
16,13 → 16,13 |
*/ |
captcha_plugin_combobox_change_callbacks.push(function(strPlugin) { |
$("#CAPTCHAPLUGIN_PARAMS_RECAPTCHA")[0].style.display = (strPlugin == 'ReCAPTCHA') ? "Block" : "None"; |
$("#CAPTCHAPLUGIN_PARAMS_RECAPTCHA")[0].style.display = (strPlugin.toLowerCase() == 'reCAPTCHA'.toLowerCase()) ? "Block" : "None"; |
}); |
rebuild_callbacks.push(function() { |
var e = $("#captcha_plugin")[0]; |
var strPlugin = e.options[e.selectedIndex].value; |
if (strPlugin != 'ReCAPTCHA') return true; |
if (strPlugin.toLowerCase() != 'reCAPTCHA'.toLowerCase()) return true; |
$("#recaptcha_public")[0].innerHTML = ''; |
$("#recaptcha_private")[0].innerHTML = ''; |
33,7 → 33,7 |
if ($("#recaptcha_public")[0].value.length == 0) |
{ |
$("#recaptcha_public_warn")[0].innerHTML = '<font color="red">'+_L('Please specify a public key!')+'</font>'; |
$("#config")[0].innerHTML = '<b><?php</b><br><br><i>// ERROR: Please specify a ReCAPTCHA public key!</i>'; // do not translate |
$("#config")[0].innerHTML = '<b><?php</b><br><br><i>// ERROR: Please specify a reCAPTCHA public key!</i>'; // do not translate |
error = true; |
} else { |
$("#recaptcha_public_warn")[0].innerHTML = ''; |
43,7 → 43,7 |
if ($("#recaptcha_private")[0].value.length == 0) |
{ |
$("#recaptcha_private_warn")[0].innerHTML = '<font color="red">'+_L('Please specify a private key!')+'</font>'; |
$("#config")[0].innerHTML = '<b><?php</b><br><br><i>// ERROR: Please specify a ReCAPTCHA private key!</i>'; // do not translate |
$("#config")[0].innerHTML = '<b><?php</b><br><br><i>// ERROR: Please specify a reCAPTCHA private key!</i>'; // do not translate |
error = true; |
} else { |
$("#recaptcha_private_warn")[0].innerHTML = ''; |
55,7 → 55,8 |
captcha_rebuild_config_callbacks.push(function() { |
var e = $("#captcha_plugin")[0]; |
var strPlugin = e.options[e.selectedIndex].value; |
if (strPlugin != 'ReCAPTCHA') return ''; |
return 'OIDplus::baseConfig()->setValue(\'RECAPTCHA_PUBLIC\', \''+$("#recaptcha_public")[0].value+'\');<br>' + |
if (strPlugin.toLowerCase() != 'reCAPTCHA'.toLowerCase()) return ''; |
return 'OIDplus::baseConfig()->setValue(\'RECAPTCHA_VERSION\', '+$("#recaptcha_version").find('option:selected').attr("name")+');<br>' + |
'OIDplus::baseConfig()->setValue(\'RECAPTCHA_PUBLIC\', \''+$("#recaptcha_public")[0].value+'\');<br>' + |
'OIDplus::baseConfig()->setValue(\'RECAPTCHA_PRIVATE\', \''+$("#recaptcha_private")[0].value+'\');<br>'; |
}); |
/trunk/plugins/viathinksoft/captcha/recaptcha/manifest.xml |
---|
23,7 → 23,6 |
</css> |
<js> |
<file>OIDplusCaptchaPluginRecaptcha.js</file> |
</js> |
<cssSetup> |
/trunk/plugins/viathinksoft/captcha/vts_challenge/OIDplusCaptchaPluginVtsClientChallenge.class.php |
---|
25,7 → 25,7 |
return 'ViaThinkSoft Client Challenge'; |
} |
public static function isVisible(): bool { |
public function isVisible(): bool { |
return false; |
} |
/trunk/plugins/viathinksoft/language/dede/messages.xml |
---|
8150,6 → 8150,14 |
</message> |
<message> |
<source><![CDATA[ |
reCAPTCHA Version |
]]></source> |
<target><![CDATA[ |
reCAPTCHA Version |
]]></target> |
</message> |
<message> |
<source><![CDATA[ |
shell access with Git client |
]]></source> |
<target><![CDATA[ |