]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - extensions/ConfirmEdit/ReCaptchaNoCaptcha/ReCaptchaNoCaptcha.class.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / extensions / ConfirmEdit / ReCaptchaNoCaptcha / ReCaptchaNoCaptcha.class.php
1 <?php
2
3 use MediaWiki\Auth\AuthenticationRequest;
4
5 class ReCaptchaNoCaptcha extends SimpleCaptcha {
6         // used for renocaptcha-edit, renocaptcha-addurl, renocaptcha-badlogin, renocaptcha-createaccount,
7         // renocaptcha-create, renocaptcha-sendemail via getMessage()
8         protected static $messagePrefix = 'renocaptcha-';
9
10         private $error = null;
11         /**
12          * Get the captcha form.
13          * @return array
14          */
15         function getFormInformation( $tabIndex = 1 ) {
16                 global $wgReCaptchaSiteKey, $wgLang;
17                 $lang = htmlspecialchars( urlencode( $wgLang->getCode() ) );
18
19                 $output = Html::element( 'div', [
20                         'class' => [
21                                 'g-recaptcha',
22                                 'mw-confirmedit-captcha-fail' => !!$this->error,
23                         ],
24                         'data-sitekey' => $wgReCaptchaSiteKey
25                 ] );
26                 $htmlUrlencoded = htmlspecialchars( urlencode( $wgReCaptchaSiteKey ) );
27                 $output .= <<<HTML
28 <noscript>
29   <div>
30     <div style="width: 302px; height: 422px; position: relative;">
31       <div style="width: 302px; height: 422px; position: absolute;">
32         <iframe src="https://www.google.com/recaptcha/api/fallback?k={$htmlUrlencoded}&hl={$lang}"
33                 frameborder="0" scrolling="no"
34                 style="width: 302px; height:422px; border-style: none;">
35         </iframe>
36       </div>
37     </div>
38     <div style="width: 300px; height: 60px; border-style: none;
39                 bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
40                 background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
41       <textarea id="g-recaptcha-response" name="g-recaptcha-response"
42                 class="g-recaptcha-response"
43                 style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
44                        margin: 10px 25px; padding: 0px; resize: none;" >
45       </textarea>
46     </div>
47   </div>
48 </noscript>
49 HTML;
50                 return [
51                         'html' => $output,
52                         'headitems' => [
53                                 // Insert reCAPTCHA script, in display language, if available.
54                                 // Language falls back to the browser's display language.
55                                 // See https://developers.google.com/recaptcha/docs/faq
56                                 "<script src=\"https://www.google.com/recaptcha/api.js?hl={$lang}\" async defer></script>"
57                         ]
58                 ];
59         }
60
61         /**
62          * @param $info
63          */
64         protected function logCheckError( $info ) {
65                 if ( $info instanceof Status ) {
66                         $errors = $info->getErrorsArray();
67                         $error = $errors[0][0];
68                 } elseif ( is_array( $info ) ) {
69                         $error = implode( ',', $info );
70                 } else {
71                         $error = $info;
72                 }
73
74                 wfDebugLog( 'captcha', 'Unable to validate response: ' . $error );
75         }
76
77         /**
78          * @param WebRequest $request
79          * @return array
80          */
81         protected function getCaptchaParamsFromRequest( WebRequest $request ) {
82                 $index = 'not used'; // ReCaptchaNoCaptcha combines captcha ID + solution into a single value
83                 // API is hardwired to return captchaWord, so use that if the standard isempty
84                 $response = $request->getVal( 'g-recaptcha-response', $request->getVal( 'captchaWord' ) );
85                 return [ $index, $response ];
86         }
87
88         /**
89          * Check, if the user solved the captcha.
90          *
91          * Based on reference implementation:
92          * https://github.com/google/recaptcha#php
93          *
94          * @param $_ mixed Not used (ReCaptcha v2 puts index and solution in a single string)
95          * @param $word string captcha solution
96          * @return bool
97          */
98         function passCaptcha( $_, $word ) {
99                 global $wgRequest, $wgReCaptchaSecretKey, $wgReCaptchaSendRemoteIP;
100
101                 $url = 'https://www.google.com/recaptcha/api/siteverify';
102                 // Build data to append to request
103                 $data = [
104                         'secret' => $wgReCaptchaSecretKey,
105                         'response' => $word,
106                 ];
107                 if ( $wgReCaptchaSendRemoteIP ) {
108                         $data['remoteip'] = $wgRequest->getIP();
109                 }
110                 $url = wfAppendQuery( $url, $data );
111                 $request = MWHttpRequest::factory( $url, [ 'method' => 'GET' ] );
112                 $status = $request->execute();
113                 if ( !$status->isOK() ) {
114                         $this->error = 'http';
115                         $this->logCheckError( $status );
116                         return false;
117                 }
118                 $response = FormatJson::decode( $request->getContent(), true );
119                 if ( !$response ) {
120                         $this->error = 'json';
121                         $this->logCheckError( $this->error );
122                         return false;
123                 }
124                 if ( isset( $response['error-codes'] ) ) {
125                         $this->error = 'recaptcha-api';
126                         $this->logCheckError( $response['error-codes'] );
127                         return false;
128                 }
129
130                 return $response['success'];
131         }
132
133         /**
134          * @param array $resultArr
135          */
136         function addCaptchaAPI( &$resultArr ) {
137                 $resultArr['captcha'] = $this->describeCaptchaType();
138                 $resultArr['captcha']['error'] = $this->error;
139         }
140
141         /**
142          * @return array
143          */
144         public function describeCaptchaType() {
145                 global $wgReCaptchaSiteKey;
146                 return [
147                         'type' => 'recaptchanocaptcha',
148                         'mime' => 'image/png',
149                         'key' => $wgReCaptchaSiteKey,
150                 ];
151         }
152
153         /**
154          * Show a message asking the user to enter a captcha on edit
155          * The result will be treated as wiki text
156          *
157          * @param $action string Action being performed
158          * @return string Wikitext
159          */
160         public function getMessage( $action ) {
161                 $msg = parent::getMessage( $action );
162                 if ( $this->error ) {
163                         $msg = new RawMessage( '<div class="error">$1</div>', [ $msg ] );
164                 }
165                 return $msg;
166         }
167
168         /**
169          * @param ApiBase $module
170          * @param array $params
171          * @param int $flags
172          * @return bool
173          */
174         public function APIGetAllowedParams( &$module, &$params, $flags ) {
175                 if ( $flags && $this->isAPICaptchaModule( $module ) ) {
176                         $params['g-recaptcha-response'] = [
177                                 ApiBase::PARAM_HELP_MSG => 'renocaptcha-apihelp-param-g-recaptcha-response',
178                         ];
179                 }
180
181                 return true;
182         }
183
184         public function getError() {
185                 return $this->error;
186         }
187
188         public function storeCaptcha( $info ) {
189                 // ReCaptcha is stored by Google; the ID will be generated at that time as well, and
190                 // the one returned here won't be used. Just pretend this worked.
191                 return 'not used';
192         }
193
194         public function retrieveCaptcha( $index ) {
195                 // just pretend it worked
196                 return [ 'index' => $index ];
197         }
198
199         public function getCaptcha() {
200                 // ReCaptcha is handled by frontend code + an external provider; nothing to do here.
201                 return [];
202         }
203
204         /**
205          * @param array $captchaData
206          * @param string $id
207          * @return Message
208          */
209         public function getCaptchaInfo( $captchaData, $id ) {
210                 return wfMessage( 'renocaptcha-info' );
211         }
212
213         /**
214          * @return ReCaptchaNoCaptchaAuthenticationRequest
215          */
216         public function createAuthenticationRequest() {
217                 return new ReCaptchaNoCaptchaAuthenticationRequest();
218         }
219
220         /**
221          * @param array $requests
222          * @param array $fieldInfo
223          * @param array $formDescriptor
224          * @param string $action
225          */
226         public function onAuthChangeFormFields(
227                 array $requests, array $fieldInfo, array &$formDescriptor, $action
228         ) {
229                 global $wgReCaptchaSiteKey;
230
231                 $req = AuthenticationRequest::getRequestByClass( $requests,
232                         CaptchaAuthenticationRequest::class, true );
233                 if ( !$req ) {
234                         return;
235                 }
236
237                 // ugly way to retrieve error information
238                 $captcha = ConfirmEditHooks::getInstance();
239
240                 $formDescriptor['captchaWord'] = [
241                         'class' => HTMLReCaptchaNoCaptchaField::class,
242                         'key' => $wgReCaptchaSiteKey,
243                         'error' => $captcha->getError(),
244                 ] + $formDescriptor['captchaWord'];
245         }
246 }