Test Failed
Pull Request — master (#21)
by
unknown
09:33
created

ReCaptchaBuilder::setLanguage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright (c) 2017 - present
4
 * LaravelGoogleRecaptcha - ReCaptchaBuilder.php
5
 * author: Roberto Belotti - [email protected]
6
 * web : robertobelotti.com, github.com/biscolab
7
 * Initial version created on: 12/9/2018
8
 * MIT license: https://github.com/biscolab/laravel-recaptcha/blob/master/LICENSE
9
 */
10
11
namespace Biscolab\ReCaptcha;
12
13
use Exception;
14
15
class ReCaptchaBuilder {
16
17
	/**
18
	 * The Site key
19
	 * please visit https://developers.google.com/recaptcha/docs/start
20
	 * @var string
21
	 */
22
	protected $api_site_key;
23
24
	/**
25
	 * The Secret key
26
	 * please visit https://developers.google.com/recaptcha/docs/start
27
	 * @var string
28
	 */
29
	protected $api_secret_key;
30
31
	/**
32
	 * The chosen ReCAPTCHA version
33
	 * please visit https://developers.google.com/recaptcha/docs/start
34
	 * @var string
35
	 */
36
	protected $version;
37
38
	/**
39
	 * The chosen ReCAPTCHA language
40
	 * please visit https://developers.google.com/recaptcha/docs/language
41
	 * @var string
42
	 */
43
	protected $language;
44
45
	/**
46
	 * Whether is true the ReCAPTCHA is inactive
47
	 * @var boolean
48
	 */
49 14
	protected $skip_by_ip = false;
50
51 14
	/**
52 14
	 * The API request URI
53 14
	 */
54 14
	protected $api_url = 'https://www.google.com/recaptcha/api/siteverify';
55 14
56
	public function __construct($api_site_key, $api_secret_key, $version = 'v2', $language = 'en') {
57
58
		$this->setApiSiteKey($api_site_key);
59
		$this->setApiSecretKey($api_secret_key);
60
		$this->setVersion($version);
61
		$this->setLanguage($language);
62 14
		$this->setSkipByIp($this->skipByIp());
63
	}
64 14
65
	/**
66 14
	 * @param string $api_site_key
67
	 *
68
	 * @return ReCaptchaBuilder
69
	 */
70
	public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder {
71
72
		$this->api_site_key = $api_site_key;
73
74 14
		return $this;
75
	}
76 14
77
	/**
78 14
	 * @param string $api_secret_key
79
	 *
80
	 * @return ReCaptchaBuilder
81
	 */
82
	public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder {
83
84
		$this->api_secret_key = $api_secret_key;
85
86 14
		return $this;
87
	}
88 14
89
	/**
90 14
	 * @param string $version
91
	 *
92
	 * @return ReCaptchaBuilder
93
	 */
94
	public function setVersion(string $version): ReCaptchaBuilder {
95
96 1
		$this->version = $version;
97
98 1
		return $this;
99
	}
100
101
	/**
102
	 * @param string $language
103
	 *
104
	 * @return ReCaptchaBuilder
105
	 */
106 14
	public function setLanguage(string $language): ReCaptchaBuilder {
107
108 14
		$this->language = $language;
109
110 14
		return $this;
111
	}
112
113
	/**
114
	 * @return string
115
	 */
116 14
	public function getVersion(): string {
117 14
118
		return $this->version;
119 14
	}
120 1
121
	/**
122
	 * @return string
123 14
	 */
124
	public function getLanguage(): string {
125
126
		return $this->language;
127
	}
128
129
	/**
130
	 * @param bool $skip_by_ip
131 14
	 *
132
	 * @return ReCaptchaBuilder
133 14
	 */
134
	public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder {
135
136
		$this->skip_by_ip = $skip_by_ip;
137
138
		return $this;
139
	}
140
141
	/**
142
	 * @return array|mixed
143
	 */
144
	public function getIpWhitelist() {
145
		$whitelist = config('recaptcha.skip_ip', []);
146 6
147
		if(!is_array($whitelist)) {
148 6
			$whitelist = explode(',', $whitelist);
149
		}
150
151
		return $whitelist;
152 6
	}
153 6
154 5
	/**
155 5
	 * Checks whether the user IP address is among IPs "to be skipped"
156
	 *
157 1
	 * @return boolean
158
	 */
159
	public function skipByIp(): bool {
160 6
161
		return (in_array(request()->ip(), $this->getIpWhitelist()));
162
	}
163
164
	/**
165
	 * Write script HTML tag in you HTML code
166
	 * Insert before </head> tag
167
	 *
168
	 * @param string|null $formId
169
	 * @param array|null  $configuration
170 6
	 *
171
	 * @return string
172 5
	 * @throws Exception
173
	 */
174 5
	public function htmlScriptTagJsApi(?string $formId = '', ?array $configuration = []): string {
175
176
		if ($this->skip_by_ip) {
177 5
			return '';
178
		}
179 1
180
		switch ($this->version) {
181
			case 'v3':
182
				$html = "<script src=\"https://www.google.com/recaptcha/api.js?hl={$this->language}&render={$this->api_site_key}\"></script>";
183 4
				break;
184 4
			default:
185
				$html = "<script src=\"https://www.google.com/recaptcha/api.js?hl={$this->language}\" async defer></script>";
186 4
		}
187 4
188
		if ($this->version == 'invisible') {
189
			if (!$formId) {
190 4
				throw new Exception("formId required", 1);
191
			}
192
			$html .= '<script>
193
		       function biscolabLaravelReCaptcha(token) {
194
		         document.getElementById("' . $formId . '").submit();
195
		       }
196
		     </script>';
197 4
		}
198
		elseif ($this->version == 'v3') {
199
200 4
			$action = array_get($configuration, 'action', 'homepage');
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

200
			$action = /** @scrutinizer ignore-deprecated */ array_get($configuration, 'action', 'homepage');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
201
202
			$js_custom_validation = array_get($configuration, 'custom_validation', '');
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

202
			$js_custom_validation = /** @scrutinizer ignore-deprecated */ array_get($configuration, 'custom_validation', '');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
203
204
			// Check if set custom_validation. That function will override default fetch validation function
205
			if ($js_custom_validation) {
206
207 5
				$validate_function = ($js_custom_validation) ? "{$js_custom_validation}(token);" : '';
208 5
			}
209
			else {
210
211
				$js_then_callback = array_get($configuration, 'callback_then', '');
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

211
				$js_then_callback = /** @scrutinizer ignore-deprecated */ array_get($configuration, 'callback_then', '');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
212
				$js_callback_catch = array_get($configuration, 'callback_catch', '');
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

212
				$js_callback_catch = /** @scrutinizer ignore-deprecated */ array_get($configuration, 'callback_catch', '');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
213
214 6
				$js_then_callback = ($js_then_callback) ? "{$js_then_callback}(response)" : '';
215
				$js_callback_catch = ($js_callback_catch) ? "{$js_callback_catch}(err)" : '';
216
217
				$validate_function = "
218
                fetch('/" . config('recaptcha.default_validation_route', 'biscolab-recaptcha/validate') . "?" . config('recaptcha.default_token_parameter_name', 'token') . "=' + token, {
219
                    headers: {
220
                        \"X-Requested-With\": \"XMLHttpRequest\",
221
                        \"X-CSRF-TOKEN\": csrfToken.content
222 5
                    }
223
                })
224 5
                .then(function(response) {
225
                   	{$js_then_callback}
226
                })
227
                .catch(function(err) {
228
                    {$js_callback_catch}
229
                });";
230
			}
231
232
			$html .= "<script>
233
                    var csrfToken = document.head.querySelector('meta[name=\"csrf-token\"]');
234 1
                  grecaptcha.ready(function() {
235
                      grecaptcha.execute('{$this->api_site_key}', {action: '{$action}'}).then(function(token) {
236 1
                        {$validate_function}
237
                      });
238
                  });
239
		     </script>";
240
		}
241
242
		return $html;
243
	}
244
245
	/**
246
	 * @param array|null $configuration
247
	 *
248
	 * @return string
249 1
	 */
250 1
	public function htmlScriptTagJsApiV3(?array $configuration = []): string {
251 1
252 1
		return $this->htmlScriptTagJsApi('', $configuration);
253
	}
254
255 1
	/**
256
	 * Call out to reCAPTCHA and process the response
257 1
	 *
258 1
	 * @param string $response
259 1
	 *
260 1
	 * @return boolean|array
261 1
	 */
262 1
	public function validate($response) {
263 1
264
		if ($this->skip_by_ip) {
265
			if ($this->returnArray()) {
266
				// Add 'skip_by_ip' field to response
267
				return [
268
					'skip_by_ip' => true,
269 1
					'score'      => 0.9,
270
					'success'    => true
271
				];
272
			}
273
274
			return true;
275
		}
276
277
		$params = http_build_query([
278
			'secret'   => $this->api_secret_key,
279
			'remoteip' => request()->getClientIp(),
280
			'response' => $response,
281 1
		]);
282
283 1
		$url = $this->api_url . '?' . $params;
284 1
285
		if (function_exists('curl_version')) {
286
			$curl = curl_init($url);
287
			curl_setopt($curl, CURLOPT_HEADER, false);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

287
			curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_HEADER, false);
Loading history...
288
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
289
			curl_setopt($curl, CURLOPT_TIMEOUT, 1);
290
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
291
			$curl_response = curl_exec($curl);
0 ignored issues
show
Bug introduced by
It seems like $curl can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
			$curl_response = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
292
		}
293
		else {
294 1
			$curl_response = file_get_contents($url);
295
		}
296 1
297
		if (is_null($curl_response) || empty($curl_response)) {
298
			if ($this->returnArray()) {
299
				// Add 'error' field to response
300
				return [
301
					'error'   => 'cURL response empty',
302
					'score'   => 0.1,
303
					'success' => false
304
				];
305
			}
306
307
			return false;
308
		}
309
		$response = json_decode(trim($curl_response), true);
310
311
		if ($this->returnArray()) {
312
			return $response;
313
		}
314
315
		return $response['success'];
316
317
	}
318
319
	/**
320
	 * @return bool
321
	 */
322
	protected function returnArray(): bool {
323
324
		return ($this->version == 'v3');
325
	}
326
}