You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
4.4 KiB
211 lines
4.4 KiB
<?php
|
|
namespace nzedb;
|
|
|
|
use app\models\Settings;
|
|
use ReCaptcha\ReCaptcha;
|
|
|
|
class Captcha
|
|
{
|
|
/**
|
|
* Smarty $page
|
|
*
|
|
* @var \Page
|
|
*/
|
|
private $page;
|
|
|
|
/**
|
|
* ReCaptcha Site Key from the
|
|
* settings database.
|
|
*
|
|
* @var bool|string
|
|
*/
|
|
private $sitekey;
|
|
|
|
/**
|
|
* ReCaptcha Secret Key from the
|
|
* settings database.
|
|
*
|
|
* @var bool|string
|
|
*/
|
|
private $secretkey;
|
|
|
|
/**
|
|
* ReCaptcha instance if enabled.
|
|
*
|
|
* @var \ReCaptcha\ReCaptcha
|
|
*/
|
|
private $recaptcha;
|
|
|
|
/**
|
|
* Contains the error output if ReCaptcha
|
|
* validation fails.
|
|
*
|
|
* @var string|bool
|
|
*/
|
|
private $error = false;
|
|
|
|
/**
|
|
* $_POST key for the user-supplied ReCaptcha response.
|
|
*/
|
|
const RECAPTCHA_POSTKEY = 'g-recaptcha-response';
|
|
|
|
/**
|
|
* Error key literals.
|
|
*/
|
|
const RECAPTCHA_ERROR_MISSING_SECRET = 'missing-input-secret';
|
|
|
|
const RECAPTCHA_ERROR_INVALID_SECRET = 'invalid-input-secret';
|
|
|
|
const RECAPTCHA_ERROR_MISSING_RESPONSE = 'missing-input-response';
|
|
|
|
const RECAPTCHA_ERROR_INVALID_RESPONSE = 'invalid-input-response';
|
|
|
|
/**
|
|
* Settings key literals
|
|
*/
|
|
const RECAPTCHA_SETTING_SITEKEY = 'APIs.recaptcha.sitekey';
|
|
|
|
const RECAPTCHA_SETTING_SECRETKEY = 'APIs.recaptcha.secretkey';
|
|
|
|
const RECAPTCHA_SETTING_ENABLED = 'APIs.recaptcha.enabled';
|
|
|
|
/**
|
|
* Construct and decide whether to show the captcha or not.
|
|
*
|
|
* @note Passing $page by reference to setup smarty vars easily.
|
|
*
|
|
* @param \Page $page
|
|
*/
|
|
public function __construct(&$page)
|
|
{
|
|
if (!$page instanceof \Page) {
|
|
throw new \InvalidArgumentException('Invalid Page variable provided');
|
|
}
|
|
|
|
$this->page = $page;
|
|
|
|
if ($this->shouldDisplay()) {
|
|
$this->page->smarty->assign('showCaptcha', true);
|
|
$this->page->smarty->assign('sitekey', $this->sitekey);
|
|
|
|
if ($this->page->isPostBack()) {
|
|
if (!$this->processCaptcha($_POST, $_SERVER['REMOTE_ADDR'])) {
|
|
$this->page->smarty->assign('error', $this->getError());
|
|
}
|
|
//Delete this key after using so it doesn't interfere with normal $_POST
|
|
//processing. (i.e. contact-us)
|
|
unset($_POST[Captcha::RECAPTCHA_POSTKEY]);
|
|
}
|
|
} else {
|
|
$this->page->smarty->assign('showCaptcha', false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If site admin setup keys properly, allow display of recaptcha.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function shouldDisplay() : bool
|
|
{
|
|
return $this->bootstrapCaptcha() === true;
|
|
}
|
|
|
|
/**
|
|
* Return formatted error messages.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getError()
|
|
{
|
|
return $this->error;
|
|
}
|
|
|
|
/**
|
|
* Process the submitted captcha and validate.
|
|
*
|
|
* @param array $response
|
|
* @param string $ip
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function processCaptcha($response, $ip)
|
|
{
|
|
if (isset($response[self::RECAPTCHA_POSTKEY])) {
|
|
$post_response = $response[self::RECAPTCHA_POSTKEY];
|
|
} else {
|
|
$post_response = '';
|
|
}
|
|
|
|
$verify_response = $this->recaptcha->verify($post_response, $ip);
|
|
|
|
if (!$verify_response->isSuccess()) {
|
|
$this->_handleErrors($verify_response->getErrorCodes());
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Build formatted error string for output using
|
|
* Google's reCaptcha error codes.
|
|
*
|
|
* @param array $codes
|
|
*/
|
|
private function _handleErrors($codes)
|
|
{
|
|
$rc_error = 'ReCaptcha Failed: ';
|
|
|
|
foreach ($codes as $c) {
|
|
switch ($c) {
|
|
case self::RECAPTCHA_ERROR_MISSING_SECRET:
|
|
$rc_error .= 'Missing Secret Key';
|
|
break;
|
|
case self::RECAPTCHA_ERROR_INVALID_SECRET:
|
|
$rc_error .= 'Invalid Secret Key';
|
|
break;
|
|
case self::RECAPTCHA_ERROR_MISSING_RESPONSE:
|
|
$rc_error .= 'No Response!';
|
|
break;
|
|
case self::RECAPTCHA_ERROR_INVALID_RESPONSE:
|
|
$rc_error .= 'Invalid response! You are a bot!';
|
|
break;
|
|
default:
|
|
$rc_error .= 'Unknown Error!';
|
|
}
|
|
}
|
|
|
|
$this->error = $rc_error;
|
|
}
|
|
|
|
/**
|
|
* Instantiate the ReCaptcha library and store it.
|
|
* Return bool on success/failure.
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function bootstrapCaptcha() : bool
|
|
{
|
|
if ($this->recaptcha instanceof ReCaptcha) {
|
|
return true;
|
|
}
|
|
|
|
$enabled = Settings::value(self::RECAPTCHA_SETTING_ENABLED);
|
|
if (!empty($enabled) && $enabled > 0 ) {// Only disable if the setting exists and is truish.
|
|
$this->sitekey = Settings::value(self::RECAPTCHA_SETTING_SITEKEY);
|
|
$this->secretkey = Settings::value(self::RECAPTCHA_SETTING_SECRETKEY);
|
|
|
|
if ($this->sitekey != false && $this->sitekey != '') {
|
|
if ($this->secretkey != false && $this->secretkey != '') {
|
|
$this->recaptcha = new ReCaptcha($this->secretkey);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|