Passed
Pull Request — master (#43)
by
unknown
06:15
created

ReCaptchaBuilder::setSkipByIp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
ccs 3
cts 3
cp 1
crap 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 Illuminate\Support\Arr;
14
15
/**
16
 * Class ReCaptchaBuilder
17
 * @package Biscolab\ReCaptcha
18
 */
19
class ReCaptchaBuilder
20
{
21
22
    /**
23
     * @var string
24
     */
25
    const DEFAULT_API_VERSION = 'v2';
26
27
    /**
28
     * @var int
29
     */
30
    const DEFAULT_CURL_TIMEOUT = 10;
31
32
    /**
33
     * @var string
34
     */
35
    const DEFAULT_ONLOAD_JS_FUNCTION = 'biscolabOnloadCallback';
36
37
    /**
38
     * @var string
39
     */
40
    const DEFAULT_RECAPTCHA_RULE_NAME = 'recaptcha';
41
42
    /**
43
     * @var string
44
     */
45
    const DEFAULT_RECAPTCHA_FIELD_NAME = 'g-recaptcha-response';
46
47
    /**
48
     * The Site key
49
     * please visit https://developers.google.com/recaptcha/docs/start
50
     * @var string
51
     */
52
    protected $api_site_key;
53
54
    /**
55
     * The Secret key
56
     * please visit https://developers.google.com/recaptcha/docs/start
57
     * @var string
58
     */
59
    protected $api_secret_key;
60
61
    /**
62
     * The chosen ReCAPTCHA version
63
     * please visit https://developers.google.com/recaptcha/docs/start
64
     * @var string
65
     */
66
    protected $version;
67
68
    /**
69
     * Whether is true the ReCAPTCHA is inactive
70
     * @var boolean
71
     */
72
    protected $skip_by_ip = false;
73
74
    /**
75
     * The API request URI
76
     */
77
    protected $api_url = 'https://www.google.com/recaptcha/api/siteverify';
78
79
    /**
80
     * ReCaptchaBuilder constructor.
81
     *
82
     * @param string      $api_site_key
83
     * @param string      $api_secret_key
84
     * @param null|string $version
85
     */
86 43
    public function __construct(
87
        string $api_site_key,
88
        string $api_secret_key,
89
        ?string $version = self::DEFAULT_API_VERSION
90
    ) {
91
92 43
        $this->setApiSiteKey($api_site_key);
93 43
        $this->setApiSecretKey($api_secret_key);
94 43
        $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

94
        $this->setVersion(/** @scrutinizer ignore-type */ $version);
Loading history...
95 43
        $this->setSkipByIp($this->skipByIp());
96 43
        $this->setAPIURL();
97 43
    }
98
99
    /**
100
     * @param string $api_site_key
101
     *
102
     * @return ReCaptchaBuilder
103
     */
104 43
    public function setApiSiteKey(string $api_site_key): ReCaptchaBuilder
105
    {
106
107 43
        $this->api_site_key = $api_site_key;
108
109 43
        return $this;
110
    }
111
112
    /**
113
     * @param string $api_secret_key
114
     *
115
     * @return ReCaptchaBuilder
116
     */
117 43
    public function setApiSecretKey(string $api_secret_key): ReCaptchaBuilder
118
    {
119
120 43
        $this->api_secret_key = $api_secret_key;
121
122 43
        return $this;
123
    }
124
125
    /**
126
     * @return int
127
     */
128 4
    public function getCurlTimeout(): int
129
    {
130
131 4
        return config('recaptcha.curl_timeout', self::DEFAULT_CURL_TIMEOUT);
132
    }
133
134
    /**
135
     * @param string $version
136
     *
137
     * @return ReCaptchaBuilder
138
     */
139 43
    public function setVersion(string $version): ReCaptchaBuilder
140
    {
141
142 43
        $this->version = $version;
143
144 43
        return $this;
145
    }
146
147
    /**
148
     * @return string
149
     */
150 1
    public function getVersion(): string
151
    {
152
153 1
        return $this->version;
154
    }
155
156
    /**
157
     * @param bool $skip_by_ip
158
     *
159
     * @return ReCaptchaBuilder
160
     */
161 43
    public function setSkipByIp(bool $skip_by_ip): ReCaptchaBuilder
162
    {
163
164 43
        $this->skip_by_ip = $skip_by_ip;
165
166 43
        return $this;
167
    }
168
169
    /**
170
     *
171
     * @return ReCaptchaBuilder
172
     */
173 43
    public function setAPIURL(): ReCaptchaBuilder
174
    {
175 43
        $this->api_url = 'https://'.config('recaptcha.baseurl','www.google.com').'/recaptcha/api/siteverify';
176
177 43
        return $this;
178
    }
179
180
    /**
181
     * @return array|mixed
182
     */
183 43
    public function getIpWhitelist()
184
    {
185
186 43
        $whitelist = config('recaptcha.skip_ip', []);
187
188 43
        if (!is_array($whitelist)) {
189 4
            $whitelist = explode(',', $whitelist);
190
        }
191
192
        $whitelist = array_map(function ($item) {
193
194 4
            return trim($item);
195 43
        }, $whitelist);
196
197 43
        return $whitelist;
198
    }
199
200
    /**
201
     * Checks whether the user IP address is among IPs "to be skipped"
202
     *
203
     * @return boolean
204
     */
205 43
    public function skipByIp(): bool
206
    {
207
208 43
        return (in_array(request()->ip(), $this->getIpWhitelist()));
209
    }
210
211
    /**
212
     * Write script HTML tag in you HTML code
213
     * Insert before </head> tag
214
     *
215
     * @param array|null $configuration
216
     *
217
     * @return string
218
     * @throws \Exception
219
     */
220 5
    public function htmlScriptTagJsApi(?array $configuration = []): string
221
    {
222
223 5
        $query = [];
224 5
        $html = '';
225
226
        // Language: "hl" parameter
227
        // resources $configuration parameter overrides default language
228 5
        $language = Arr::get($configuration, 'lang');
229 5
        if (!$language) {
230 4
            $language = config('recaptcha.default_language', null);
231
        }
232 5
        if ($language) {
233 2
            Arr::set($query, 'hl', $language);
234
        }
235
236
        // Onload JS callback function: "onload" parameter
237
        // "render" parameter set to "explicit"
238 5
        if (config('recaptcha.explicit', null) && $this->version === 'v2') {
239 2
            Arr::set($query, 'render', 'explicit');
240 2
            Arr::set($query, 'onload', self::DEFAULT_ONLOAD_JS_FUNCTION);
241
242
            /** @scrutinizer ignore-call */
243 2
            $html = $this->getOnLoadCallback();
244
        }
245
246 4
        $baseURL = config('recaptcha.baseurl','www.google.com');
247
248
        // Create query string
249 4
        $query = ($query) ? '?' . http_build_query($query) : "";
250 4
        $html .= "<script src=\"https://".$baseURL."/recaptcha/api.js" . $query . "\" async defer></script>";
251
252 4
        return $html;
253
    }
254
255
    /**
256
     * Call out to reCAPTCHA and process the response
257
     *
258
     * @param string $response
259
     *
260
     * @return boolean|array
261
     */
262 3
    public function validate($response)
263
    {
264
265 3
        if ($this->skip_by_ip) {
266 2
            if ($this->returnArray()) {
267
                // Add 'skip_by_ip' field to response
268
                return [
269 1
                    'skip_by_ip' => true,
270
                    'score'      => 0.9,
271
                    'success'    => true
272
                ];
273
            }
274
275 1
            return true;
276
        }
277
278 1
        $params = http_build_query([
279 1
            'secret'   => $this->api_secret_key,
280 1
            'remoteip' => request()->getClientIp(),
281 1
            'response' => $response,
282
        ]);
283
284 1
        $url = $this->api_url . '?' . $params;
285
286 1
        if (function_exists('curl_version')) {
287
288 1
            $curl = curl_init($url);
289 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

289
            curl_setopt(/** @scrutinizer ignore-type */ $curl, CURLOPT_HEADER, false);
Loading history...
290 1
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
291 1
            curl_setopt($curl, CURLOPT_TIMEOUT, $this->getCurlTimeout());
292 1
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
293 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

293
            $curl_response = curl_exec(/** @scrutinizer ignore-type */ $curl);
Loading history...
294
        } else {
295
            $curl_response = file_get_contents($url);
296
        }
297
298 1
        if (is_null($curl_response) || empty($curl_response)) {
299
            if ($this->returnArray()) {
300
                // Add 'error' field to response
301
                return [
302
                    'error'   => 'cURL response empty',
303
                    'score'   => 0.1,
304
                    'success' => false
305
                ];
306
            }
307
308
            return false;
309
        }
310 1
        $response = json_decode(trim($curl_response), true);
311
312 1
        if ($this->returnArray()) {
313 1
            return $response;
314
        }
315
316
        return $response['success'];
317
318
    }
319
320
    /**
321
     * @return string
322
     */
323 1
    public function getApiSiteKey(): string
324
    {
325
326 1
        return $this->api_site_key;
327
    }
328
329
    /**
330
     * @return string
331
     */
332 1
    public function getApiSecretKey(): string
333
    {
334
335 1
        return $this->api_secret_key;
336
    }
337
338
    /**
339
     * @return bool
340
     */
341 3
    protected function returnArray(): bool
342
    {
343
344 3
        return ($this->version == 'v3');
345
    }
346
}
347