X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/extensions/ConfirmEdit/ReCaptchaNoCaptcha/ReCaptchaNoCaptcha.class.php diff --git a/extensions/ConfirmEdit/ReCaptchaNoCaptcha/ReCaptchaNoCaptcha.class.php b/extensions/ConfirmEdit/ReCaptchaNoCaptcha/ReCaptchaNoCaptcha.class.php new file mode 100644 index 00000000..15e1c108 --- /dev/null +++ b/extensions/ConfirmEdit/ReCaptchaNoCaptcha/ReCaptchaNoCaptcha.class.php @@ -0,0 +1,246 @@ +getCode() ) ); + + $output = Html::element( 'div', [ + 'class' => [ + 'g-recaptcha', + 'mw-confirmedit-captcha-fail' => !!$this->error, + ], + 'data-sitekey' => $wgReCaptchaSiteKey + ] ); + $htmlUrlencoded = htmlspecialchars( urlencode( $wgReCaptchaSiteKey ) ); + $output .= << +
+
+
+ +
+
+
+ +
+
+ +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 + "" + ] + ]; + } + + /** + * @param $info + */ + 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 ); + } + + /** + * @param WebRequest $request + * @return array + */ + 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 bool + */ + 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']; + } + + /** + * @param array $resultArr + */ + function addCaptchaAPI( &$resultArr ) { + $resultArr['captcha'] = $this->describeCaptchaType(); + $resultArr['captcha']['error'] = $this->error; + } + + /** + * @return array + */ + public function describeCaptchaType() { + global $wgReCaptchaSiteKey; + return [ + 'type' => 'recaptchanocaptcha', + 'mime' => 'image/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( '
$1
', [ $msg ] ); + } + return $msg; + } + + /** + * @param ApiBase $module + * @param array $params + * @param int $flags + * @return bool + */ + public function APIGetAllowedParams( &$module, &$params, $flags ) { + if ( $flags && $this->isAPICaptchaModule( $module ) ) { + $params['g-recaptcha-response'] = [ + ApiBase::PARAM_HELP_MSG => 'renocaptcha-apihelp-param-g-recaptcha-response', + ]; + } + + 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 []; + } + + /** + * @param array $captchaData + * @param string $id + * @return Message + */ + public function getCaptchaInfo( $captchaData, $id ) { + return wfMessage( 'renocaptcha-info' ); + } + + /** + * @return ReCaptchaNoCaptchaAuthenticationRequest + */ + public function createAuthenticationRequest() { + return new ReCaptchaNoCaptchaAuthenticationRequest(); + } + + /** + * @param array $requests + * @param array $fieldInfo + * @param array $formDescriptor + * @param string $action + */ + 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']; + } +}