mediawiki-extensions-Confir.../ReCaptchaNoCaptcha/ReCaptchaNoCaptcha.class.php
Florian e566dcb966 Remove getForm() and replace by getFormInformation()
This commit removes SimpleCaptcha::getForm() and replaces it by its more informative
counterpart getFormInformation(), which returns an array, which provides some
more information about the form than only the html.

The information included in the array is:
 * html: The HTML of the CAPTCHA form (this is the same as what you expected from
   getForm() previously)
 * modules: ResourceLoader modules, if any, that should be added to the output of the
   page
 * modulestyles: ResourceLoader style modules, if any, that should be added to th
   output of the page
 * headitems: Head items that should be added to the output (see OutputPage::addHeadItems)

Mostly you shouldn't need to handle the response of getFormInformation() anymore, as there's
a new function, addFormToOutput(), which takes an instance of OutputPage as a first parameter
and handles the response of getFormInformation for you (adds all information to the given
OutputPage instance, if they're provided).

Bug: T141300
Depends-On: I433afd124b57526caa13a540cda48ba2b99a9bde
Change-Id: I25f344538052fc18993c43185fbd97804a7cfc81
2016-08-04 01:45:13 +00:00

230 lines
6.7 KiB
PHP

<?php
use MediaWiki\Auth\AuthenticationRequest;
class ReCaptchaNoCaptcha extends SimpleCaptcha {
// used for renocaptcha-edit, renocaptcha-addurl, renocaptcha-badlogin, renocaptcha-createaccount,
// renocaptcha-create, renocaptcha-sendemail via getMessage()
protected static $messagePrefix = 'renocaptcha-';
private $error = null;
/**
* Get the captcha form.
* @return array
*/
function getFormInformation( $tabIndex = 1 ) {
global $wgReCaptchaSiteKey, $wgLang;
$lang = htmlspecialchars( urlencode( $wgLang->getCode() ) );
$output = Html::element( 'div', [
'class' => [
'g-recaptcha',
'mw-confirmedit-captcha-fail' => !!$this->error,
],
'data-sitekey' => $wgReCaptchaSiteKey
] );
$htmlUrlencoded = htmlspecialchars( urlencode( $wgReCaptchaSiteKey ) );
$output .= <<<HTML
<noscript>
<div>
<div style="width: 302px; height: 422px; position: relative;">
<div style="width: 302px; height: 422px; position: absolute;">
<iframe src="https://www.google.com/recaptcha/api/fallback?k={$htmlUrlencoded}&hl={$lang}"
frameborder="0" scrolling="no"
style="width: 302px; height:422px; border-style: none;">
</iframe>
</div>
</div>
<div style="width: 300px; height: 60px; border-style: none;
bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
<textarea id="g-recaptcha-response" name="g-recaptcha-response"
class="g-recaptcha-response"
style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
margin: 10px 25px; padding: 0px; resize: none;" >
</textarea>
</div>
</div>
</noscript>
HTML;
return [
'html' => $output,
'headitems' => [
// Insert reCAPTCHA script, in display language, if available.
// Language falls back to the browser's display language.
// See https://developers.google.com/recaptcha/docs/faq
"<script src=\"https://www.google.com/recaptcha/api.js?hl={$lang}\" async defer></script>"
]
];
}
protected function logCheckError( $info ) {
if ( $info instanceof Status ) {
$errors = $info->getErrorsArray();
$error = $errors[0][0];
} elseif ( is_array( $info ) ) {
$error = implode( ',', $info );
} else {
$error = $info;
}
wfDebugLog( 'captcha', 'Unable to validate response: ' . $error );
}
protected function getCaptchaParamsFromRequest( WebRequest $request ) {
$index = 'not used'; // ReCaptchaNoCaptcha combines captcha ID + solution into a single value
// API is hardwired to return captchaWord, so use that if the standard isempty
$response = $request->getVal( 'g-recaptcha-response', $request->getVal( 'captchaWord' ) );
return [ $index, $response ];
}
/**
* Check, if the user solved the captcha.
*
* Based on reference implementation:
* https://github.com/google/recaptcha#php
*
* @param $_ mixed Not used (ReCaptcha v2 puts index and solution in a single string)
* @param $word string captcha solution
* @return boolean
*/
function passCaptcha( $_, $word ) {
global $wgRequest, $wgReCaptchaSecretKey, $wgReCaptchaSendRemoteIP;
$url = 'https://www.google.com/recaptcha/api/siteverify';
// Build data to append to request
$data = [
'secret' => $wgReCaptchaSecretKey,
'response' => $word,
];
if ( $wgReCaptchaSendRemoteIP ) {
$data['remoteip'] = $wgRequest->getIP();
}
$url = wfAppendQuery( $url, $data );
$request = MWHttpRequest::factory( $url, [ 'method' => 'GET' ] );
$status = $request->execute();
if ( !$status->isOK() ) {
$this->error = 'http';
$this->logCheckError( $status );
return false;
}
$response = FormatJson::decode( $request->getContent(), true );
if ( !$response ) {
$this->error = 'json';
$this->logCheckError( $this->error );
return false;
}
if ( isset( $response['error-codes'] ) ) {
$this->error = 'recaptcha-api';
$this->logCheckError( $response['error-codes'] );
return false;
}
return $response['success'];
}
function addCaptchaAPI( &$resultArr ) {
$resultArr['captcha'] = $this->describeCaptchaType();
$resultArr['captcha']['error'] = $this->error;
}
public function describeCaptchaType() {
global $wgReCaptchaSiteKey;
return [
'type' => 'recaptchanocaptcha',
'mime' => 'mage/png',
'key' => $wgReCaptchaSiteKey,
];
}
/**
* Show a message asking the user to enter a captcha on edit
* The result will be treated as wiki text
*
* @param $action string Action being performed
* @return string Wikitext
*/
public function getMessage( $action ) {
$msg = parent::getMessage( $action );
if ( $this->error ) {
$msg = new RawMessage( '<div class="error">$1</div>', [ $msg ] );
}
return $msg;
}
public function APIGetAllowedParams( &$module, &$params, $flags ) {
if ( $flags && $this->isAPICaptchaModule( $module ) ) {
if ( defined( 'ApiBase::PARAM_HELP_MSG' ) ) {
$params['g-recaptcha-response'] = [
ApiBase::PARAM_HELP_MSG => 'renocaptcha-apihelp-param-g-recaptcha-response',
];
} else {
// @todo: Remove this branch when support for MediaWiki < 1.25 is dropped
$params['g-recaptcha-response'] = null;
}
}
return true;
}
/**
* @deprecated since MediaWiki 1.25
*/
public function APIGetParamDescription( &$module, &$desc ) {
if ( $this->isAPICaptchaModule( $module ) ) {
$desc['g-recaptcha-response'] = 'Field from the ReCaptcha widget';
}
return true;
}
public function getError() {
return $this->error;
}
public function storeCaptcha( $info ) {
// ReCaptcha is stored by Google; the ID will be generated at that time as well, and
// the one returned here won't be used. Just pretend this worked.
return 'not used';
}
public function retrieveCaptcha( $index ) {
// just pretend it worked
return [ 'index' => $index ];
}
public function getCaptcha() {
// ReCaptcha is handled by frontend code + an external provider; nothing to do here.
return [];
}
public function getCaptchaInfo( $captchaData, $id ) {
return wfMessage( 'renocaptcha-info' );
}
public function createAuthenticationRequest() {
return new ReCaptchaNoCaptchaAuthenticationRequest();
}
public function onAuthChangeFormFields(
array $requests, array $fieldInfo, array &$formDescriptor, $action
) {
global $wgReCaptchaSiteKey;
$req = AuthenticationRequest::getRequestByClass( $requests,
CaptchaAuthenticationRequest::class, true );
if ( !$req ) {
return;
}
// ugly way to retrieve error information
$captcha = ConfirmEditHooks::getInstance();
$formDescriptor['captchaWord'] = [
'class' => HTMLReCaptchaNoCaptchaField::class,
'key' => $wgReCaptchaSiteKey,
'error' => $captcha->getError(),
] + $formDescriptor['captchaWord'];
}
}