Test Failed
Push — master ( 2a4e3b...86163b )
by Roberto
03:30
created

ReCaptchaBuilder::returnArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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
	 * Whether is true the ReCAPTCHA is inactive
40
	 * @var boolean
41
	 */
42
	protected $skip_by_ip = false;
43
44
	/**
45
	 * The API request URI
46
	 */
47
	protected $api_url = 'https://www.google.com/recaptcha/api/siteverify';
48
49
	public function __construct($api_site_key, $api_secret_key, $version = 'v2') {
50
51
		$this->setApiSiteKey($api_site_key);
52
		$this->setApiSecretKey($api_secret_key);
53
		$this->setVersion($version);
54
		$this->setSkipByIp($this->skipByIp());
55
	}
56
57
	/**
58
	 * @param string $api_site_key
59
	 *
60
	 * @return ReCaptchaBuilder
61
	 */
62
	public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder {
63
64
		$this->api_site_key = $api_site_key;
65
66
		return $this;
67
	}
68
69
	/**
70
	 * @param string $api_secret_key
71
	 *
72
	 * @return ReCaptchaBuilder
73
	 */
74
	public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder {
75
76
		$this->api_secret_key = $api_secret_key;
77
78
		return $this;
79
	}
80
81
	/**
82
	 * @param string $version
83
	 *
84
	 * @return ReCaptchaBuilder
85
	 */
86
	public function setVersion(string $version): ReCaptchaBuilder {
87
88
		$this->version = $version;
89
90
		return $this;
91
	}
92
93
	/**
94
	 * @return string
95
	 */
96
	public function getVersion(): string {
97
98
		return $this->version;
99
	}
100
101
	/**
102
	 * @param bool $skip_by_ip
103
	 *
104
	 * @return ReCaptchaBuilder
105
	 */
106
	public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder {
107
108
		$this->skip_by_ip = $skip_by_ip;
109
110
		return $this;
111
	}
112
113
	/**
114
	 * @return array|mixed
115
	 */
116
	public function getIpWhitelist() {
117
		$whitelist = config('recaptcha.skip_ip', []);
118
119
		if(!is_array($whitelist)) {
120
			$whitelist = explode(',', $whitelist);
121
		}
122
123
		return $whitelist;
124
	}
125
126
	/**
127
	 * Checks whether the user IP address is among IPs "to be skipped"
128
	 *
129
	 * @return boolean
130
	 */
131
	public function skipByIp(): bool {
132
133
		return (in_array(request()->ip(), $this->getIpWhitelist()));
134
	}
135
136
	/**
137
	 * Write script HTML tag in you HTML code
138
	 * Insert before </head> tag
139
	 *
140
	 * @param string|null $formId
141
	 * @param array|null  $configuration
142
	 *
143
	 * @return string
144
	 * @throws Exception
145
	 */
146
	public function htmlScriptTagJsApi(?string $formId = '', ?array $configuration = []): string {
147
148
		if ($this->skip_by_ip) {
149
			return '';
150
		}
151
152
		switch ($this->version) {
153
			case 'v3':
154
				$html = "<script src=\"https://www.google.com/recaptcha/api.js?render={$this->api_site_key}\"></script>";
155
				break;
156
			default:
157
				$html = "<script src=\"https://www.google.com/recaptcha/api.js\" async defer></script>";
158
		}
159
160
		if ($this->version == 'invisible') {
161
			if (!$formId) {
162
				throw new Exception("formId required", 1);
163
			}
164
			$html .= '<script>
165
		       function biscolabLaravelReCaptcha(token) {
166
		         document.getElementById("' . $formId . '").submit();
167
		       }
168
		     </script>';
169
		}
170
		elseif ($this->version == 'v3') {
171
172
			$action = array_get($configuration, 'action', 'homepage');
173
174
			$js_custom_validation = array_get($configuration, 'custom_validation', '');
175
176
			// Check if set custom_validation. That function will override default fetch validation function
177
			if ($js_custom_validation) {
178
179
				$validate_function = ($js_custom_validation) ? "{$js_custom_validation}(token);" : '';
180
			}
181
			else {
182
183
				$js_then_callback = array_get($configuration, 'callback_then', '');
184
				$js_callback_catch = array_get($configuration, 'callback_catch', '');
185
186
				$js_then_callback = ($js_then_callback) ? "{$js_then_callback}(response)" : '';
187
				$js_callback_catch = ($js_callback_catch) ? "{$js_callback_catch}(err)" : '';
188
189
				$validate_function = "
190
                fetch('/" . config('recaptcha.default_validation_route', 'biscolab-recaptcha/validate') . "?" . config('recaptcha.default_token_parameter_name', 'token') . "=' + token, {
191
                    headers: {
192
                        \"X-Requested-With\": \"XMLHttpRequest\",
193
                        \"X-CSRF-TOKEN\": csrfToken.content
194
                    }
195
                })
196
                .then(function(response) {
197
                   	{$js_then_callback}
198
                })
199
                .catch(function(err) {
200
                    {$js_callback_catch}
201
                });";
202
			}
203
204
			$html .= "<script>
205
                    var csrfToken = document.head.querySelector('meta[name=\"csrf-token\"]');
206
                  grecaptcha.ready(function() {
207
                      grecaptcha.execute('{$this->api_site_key}', {action: '{$action}'}).then(function(token) {
208
                        {$validate_function}
209
                      });
210
                  });
211
		     </script>";
212
		}
213
214
		return $html;
215
	}
216
217
	/**
218
	 * @param array|null $configuration
219
	 *
220
	 * @return string
221
	 */
222
	public function htmlScriptTagJsApiV3(?array $configuration = []): string {
223
224
		return $this->htmlScriptTagJsApi('', $configuration);
225
	}
226
227
	/**
228
	 * Call out to reCAPTCHA and process the response
229
	 *
230
	 * @param string $response
231
	 *
232
	 * @return boolean|array
233
	 */
234
	public function validate($response) {
235
236
		if ($this->skip_by_ip) {
237
			if ($this->returnArray()) {
238
				// Add 'skip_by_ip' field to response
239
				return [
240
					'skip_by_ip' => true,
241
					'score'      => 0.9,
242
					'success'    => true
243
				];
244
			}
245
246
			return true;
247
		}
248
249
		$params = http_build_query([
250
			'secret'   => $this->api_secret_key,
251
			'remoteip' => request()->getClientIp(),
252
			'response' => $response,
253
		]);
254
255
		$url = $this->api_url . '?' . $params;
256
257
		if (function_exists('curl_version')) {
258
			$curl = curl_init($url);
259
			curl_setopt($curl, CURLOPT_HEADER, false);
260
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
261
			curl_setopt($curl, CURLOPT_TIMEOUT, 1);
262
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
263
			$curl_response = curl_exec($curl);
264
		}
265
		else {
266
			$curl_response = file_get_contents($url);
267
		}
268
269
		if (is_null($curl_response) || empty($curl_response)) {
270
			if ($this->returnArray()) {
271
				// Add 'error' field to response
272
				return [
273
					'error'   => 'cURL response empty',
274
					'score'   => 0.1,
275
					'success' => false
276
				];
277
			}
278
279
			return false;
280
		}
281
		$response = json_decode(trim($curl_response), true);
282
283
		if ($this->returnArray()) {
284
			return $response;
285
		}
286
287
		return $response['success'];
288
289
	}
290
291
	/**
292
	 * @return bool
293
	 */
294
	protected function returnArray(): bool {
295
296
		return ($this->version == 'v3');
297
	}
298
}