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'); |
|
|
|
|
201
|
|
|
|
202
|
|
|
$js_custom_validation = array_get($configuration, 'custom_validation', ''); |
|
|
|
|
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', ''); |
|
|
|
|
212
|
|
|
$js_callback_catch = array_get($configuration, 'callback_catch', ''); |
|
|
|
|
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); |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
} |
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.