Completed
Push — master ( 7b9b82...f4954a )
by Roberto
07:53 queued 11s
created

ReCaptchaBuilder::setCurlTimeout()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 8
rs 10
ccs 5
cts 5
cp 1
crap 2

1 Method

Rating   Name   Duplication   Size   Complexity  
A ReCaptchaBuilder::setVersion() 0 6 1
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
use Illuminate\Support\Arr;
15
16
/**
17
 * Class ReCaptchaBuilder
18
 * @package Biscolab\ReCaptcha
19
 */
20
class ReCaptchaBuilder
21
{
22
23
	/**
24
	 * @var string
25
	 */
26
	const DEFAULT_API_VERSION = 'v2';
27
28
	/**
29
	 * @var int
30
	 */
31
	const DEFAULT_CURL_TIMEOUT = 10;
32
33
	/**
34
	 * The Site key
35
	 * please visit https://developers.google.com/recaptcha/docs/start
36
	 * @var string
37
	 */
38
	protected $api_site_key;
39
40
	/**
41
	 * The Secret key
42
	 * please visit https://developers.google.com/recaptcha/docs/start
43
	 * @var string
44
	 */
45
	protected $api_secret_key;
46
47
	/**
48
	 * The chosen ReCAPTCHA version
49
	 * please visit https://developers.google.com/recaptcha/docs/start
50
	 * @var string
51
	 */
52
	protected $version;
53
54
	/**
55
	 * Whether is true the ReCAPTCHA is inactive
56
	 * @var boolean
57
	 */
58
	protected $skip_by_ip = false;
59
60
	/**
61
	 * The API request URI
62
	 */
63
	protected $api_url = 'https://www.google.com/recaptcha/api/siteverify';
64
65
	/**
66
	 * ReCaptchaBuilder constructor.
67
	 *
68
	 * @param string      $api_site_key
69
	 * @param string      $api_secret_key
70
	 * @param null|string $version
71
	 */
72 31
	public function __construct(
73
		string $api_site_key,
74
		string $api_secret_key,
75
		?string $version = self::DEFAULT_API_VERSION
76
	) {
77
78 31
		$this->setApiSiteKey($api_site_key);
79 31
		$this->setApiSecretKey($api_secret_key);
80 31
		$this->setVersion($version);
0 ignored issues
show
Bug introduced by
It seems like $version can also be of type null; however, parameter $version of Biscolab\ReCaptcha\ReCaptchaBuilder::setVersion() does only seem to accept string, 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

80
		$this->setVersion(/** @scrutinizer ignore-type */ $version);
Loading history...
81 31
		$this->setSkipByIp($this->skipByIp());
82 31
	}
83
84
	/**
85
	 * @param string $api_site_key
86
	 *
87
	 * @return ReCaptchaBuilder
88
	 */
89 31
	public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
90
	{
91
92 31
		$this->api_site_key = $api_site_key;
93
94 31
		return $this;
95
	}
96
97
	/**
98
	 * @param string $api_secret_key
99
	 *
100
	 * @return ReCaptchaBuilder
101
	 */
102 31
	public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
103
	{
104
105 31
		$this->api_secret_key = $api_secret_key;
106
107 31
		return $this;
108
	}
109
110
	/**
111
	 * @return int
112
	 */
113 4
	public function getCurlTimeout(): int
114
	{
115
116 4
		return config('recaptcha.curl_timeout', self::DEFAULT_CURL_TIMEOUT);
117
	}
118
119
	/**
120
	 * @param string $version
121
	 *
122
	 * @return ReCaptchaBuilder
123
	 */
124 31
	public function setVersion(string $version): ReCaptchaBuilder
125
	{
126
127 31
		$this->version = $version;
128
129 31
		return $this;
130
	}
131
132
	/**
133
	 * @return string
134
	 */
135 1
	public function getVersion(): string
136
	{
137
138 1
		return $this->version;
139
	}
140
141
	/**
142
	 * @param bool $skip_by_ip
143
	 *
144
	 * @return ReCaptchaBuilder
145
	 */
146 31
	public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
147
	{
148
149 31
		$this->skip_by_ip = $skip_by_ip;
150
151 31
		return $this;
152
	}
153
154
	/**
155
	 * @return array|mixed
156
	 */
157 31
	public function getIpWhitelist()
158
	{
159
160 31
		$whitelist = config('recaptcha.skip_ip', []);
161
162 31
		if (!is_array($whitelist)) {
163 4
			$whitelist = explode(',', $whitelist);
164
		}
165
166 31
		return $whitelist;
167
	}
168
169
	/**
170
	 * Checks whether the user IP address is among IPs "to be skipped"
171
	 *
172
	 * @return boolean
173
	 */
174 31
	public function skipByIp(): bool
175
	{
176
177 31
		return (in_array(request()->ip(), $this->getIpWhitelist()));
178
	}
179
180
	/**
181
	 * Write script HTML tag in you HTML code
182
	 * Insert before </head> tag
183
	 *
184
	 * @param string|null $formId
185
	 * @param array|null  $configuration
186
	 *
187
	 * @return string
188
	 * @throws Exception
189
	 */
190 8
	public function htmlScriptTagJsApi(?string $formId = '', ?array $configuration = []): string
191
	{
192
193 8
		if ($this->skip_by_ip) {
194
			return '';
195
		}
196
197 8
		switch ($this->version) {
198 8
			case 'v3':
199 5
				$html = "<script src=\"https://www.google.com/recaptcha/api.js?render={$this->api_site_key}\"></script>";
200 5
				break;
201
			default:
202 3
				$language = config('recaptcha.default_language', null);
203 3
				$query = ($language) ? "?hl=" . $language : "";
204 3
				$html = "<script src=\"https://www.google.com/recaptcha/api.js" . $query . "\" async defer></script>";
205
		}
206
207 8
		if ($this->version == 'invisible') {
208 1
			if (!$formId) {
209 1
				throw new Exception("formId required", 1);
210
			}
211
			$html .= '<script>
212
		       function biscolabLaravelReCaptcha(token) {
213
		         document.getElementById("' . $formId . '").submit();
214
		       }
215
		     </script>';
216 7
		} elseif ($this->version == 'v3') {
217
218 5
			$action = Arr::get($configuration, 'action', 'homepage');
219
220 5
			$js_custom_validation = Arr::get($configuration, 'custom_validation', '');
221
222
			// Check if set custom_validation. That function will override default fetch validation function
223 5
			if ($js_custom_validation) {
224
225 1
				$validate_function = ($js_custom_validation) ? "{$js_custom_validation}(token);" : '';
226
			} else {
227
228 4
				$js_then_callback = Arr::get($configuration, 'callback_then', '');
229 4
				$js_callback_catch = Arr::get($configuration, 'callback_catch', '');
230
231 4
				$js_then_callback = ($js_then_callback) ? "{$js_then_callback}(response)" : '';
232 4
				$js_callback_catch = ($js_callback_catch) ? "{$js_callback_catch}(err)" : '';
233
234
				$validate_function = "
235 4
                fetch('/" . config('recaptcha.default_validation_route',
236 4
						'biscolab-recaptcha/validate') . "?" . config('recaptcha.default_token_parameter_name',
237 4
						'token') . "=' + token, {
238
                    headers: {
239
                        \"X-Requested-With\": \"XMLHttpRequest\",
240
                        \"X-CSRF-TOKEN\": csrfToken.content
241
                    }
242
                })
243
                .then(function(response) {
244 4
                   	{$js_then_callback}
245
                })
246
                .catch(function(err) {
247 4
                    {$js_callback_catch}
248
                });";
249
			}
250
251
			$html .= "<script>
252
                    var csrfToken = document.head.querySelector('meta[name=\"csrf-token\"]');
253
                  grecaptcha.ready(function() {
254 5
                      grecaptcha.execute('{$this->api_site_key}', {action: '{$action}'}).then(function(token) {
255 5
                        {$validate_function}
256
                      });
257
                  });
258
		     </script>";
259
		}
260
261 7
		return $html;
262
	}
263
264
	/**
265
	 * @param array|null $configuration
266
	 *
267
	 * @return string
268
	 */
269 5
	public function htmlScriptTagJsApiV3(?array $configuration = []): string
270
	{
271
272 5
		return $this->htmlScriptTagJsApi('', $configuration);
273
	}
274
275
	/**
276
	 * Call out to reCAPTCHA and process the response
277
	 *
278
	 * @param string $response
279
	 *
280
	 * @return boolean|array
281
	 */
282 3
	public function validate($response)
283
	{
284
285 3
		if ($this->skip_by_ip) {
286 2
			if ($this->returnArray()) {
287
				// Add 'skip_by_ip' field to response
288
				return [
289 1
					'skip_by_ip' => true,
290
					'score'      => 0.9,
291
					'success'    => true
292
				];
293
			}
294
295 1
			return true;
296
		}
297
298 1
		$params = http_build_query([
299 1
			'secret'   => $this->api_secret_key,
300 1
			'remoteip' => request()->getClientIp(),
301 1
			'response' => $response,
302
		]);
303
304 1
		$url = $this->api_url . '?' . $params;
305
306 1
		if (function_exists('curl_version')) {
307
308 1
			$curl = curl_init($url);
309 1
			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

309
			curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_HEADER, false);
Loading history...
310 1
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
311 1
			curl_setopt($curl, CURLOPT_TIMEOUT, $this->getCurlTimeout());
312 1
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
313 1
			$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

313
			$curl_response = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
314
		} else {
315
			$curl_response = file_get_contents($url);
316
		}
317
318 1
		if (is_null($curl_response) || empty($curl_response)) {
319
			if ($this->returnArray()) {
320
				// Add 'error' field to response
321
				return [
322
					'error'   => 'cURL response empty',
323
					'score'   => 0.1,
324
					'success' => false
325
				];
326
			}
327
328
			return false;
329
		}
330 1
		$response = json_decode(trim($curl_response), true);
331
332 1
		if ($this->returnArray()) {
333 1
			return $response;
334
		}
335
336
		return $response['success'];
337
338
	}
339
340
	/**
341
	 * @return string
342
	 */
343 1
	public function getApiSiteKey(): string
344
	{
345
346 1
		return $this->api_site_key;
347
	}
348
349
	/**
350
	 * @return string
351
	 */
352 1
	public function getApiSecretKey(): string
353
	{
354
355 1
		return $this->api_secret_key;
356
	}
357
358
	/**
359
	 * @return bool
360
	 */
361 3
	protected function returnArray(): bool
362
	{
363
364 3
		return ($this->version == 'v3');
365
	}
366
}