Completed
Push — master ( 9d13eb...060bbf )
by Antonio Carlos
06:41
created

IpList::addToList()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 35
rs 6.7272
cc 7
eloc 19
nc 10
nop 3
1
<?php
2
3
namespace PragmaRX\Firewall\Repositories;
4
5
use PragmaRX\Support\IpAddress;
6
use PragmaRX\Firewall\Support\ServiceInstances;
7
use PragmaRX\Firewall\Vendor\Laravel\Models\Firewall as FirewallModel;
8
9
class IpList
10
{
11
    use ServiceInstances;
12
13
    /**
14
     * @var FirewallModel
15
     */
16
    private $model;
17
18
    /**
19
     * Create instance of DataRepository.
20
     *
21
     * @param FirewallModel $model
22
     */
23
    public function __construct(FirewallModel $model)
24
    {
25
        $this->model = $model;
26
    }
27
28
    /**
29
     * Get a list of non database ip addresses.
30
     *
31
     * @return array
32
     */
33
    private function getNonDatabaseIps()
34
    {
35
        return array_merge_recursive(
36
            array_map(function ($ip) {
37
                $ip['whitelisted'] = true;
38
39
                return $ip;
40
            }, $this->formatIpArray($this->config()->get('whitelist'))),
41
42
            array_map(function ($ip) {
43
                $ip['whitelisted'] = false;
44
45
                return $ip;
46
            }, $this->formatIpArray($this->config()->get('blacklist')))
47
        );
48
    }
49
50
51
    /**
52
     * Remove ip from all array lists.
53
     *
54
     * @param $ipAddress
55
     *
56
     * @return bool
57
     */
58
    private function removeFromArrayList($ipAddress)
59
    {
60
        return $this->removeFromArrayListType('whitelist', $ipAddress) ||
61
            $this->removeFromArrayListType('blacklist', $ipAddress);
62
    }
63
64
65
    /**
66
     * Remove the ip address from an array list.
67
     *
68
     * @param $type
69
     * @param $ipAddress
70
     *
71
     * @return bool
72
     */
73
    private function removeFromArrayListType($type, $ipAddress)
74
    {
75
        if (($key = array_search($ipAddress, $data = $this->config()->get($type))) !== false) {
76
            unset($data[$key]);
77
78
            $this->config()->set($type, $data);
79
80
            return true;
81
        }
82
83
        return false;
84
    }
85
86
    /**
87
     * Remove ip from database.
88
     *
89
     * @param $ipAddress
90
     *
91
     * @return bool
92
     */
93
    private function removeFromDatabaseList($ipAddress)
94
    {
95
        if ($ip = $this->find($ipAddress)) {
96
            $ip->delete();
0 ignored issues
show
Bug introduced by
The call to PragmaRX\Firewall\Repositories\IpList::delete() has too few arguments starting with ipAddress. ( Ignorable by Annotation )

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

96
            /** @scrutinizer ignore-call */ $ip->delete();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
97
98
            $this->cache()->forget($ipAddress);
99
100
            return true;
101
        }
102
103
        return false;
104
    }
105
106
    /**
107
     * Transform a list of ips to a list of models.
108
     *
109
     * @param $ipList
110
     *
111
     * @return \Illuminate\Support\Collection
112
     */
113
    private function toModels($ipList)
114
    {
115
        $ips = [];
116
117
        foreach ($ipList as $ip) {
118
            $ips[] = $this->makeModel($ip);
119
        }
120
121
        return collect($ips);
122
    }
123
124
    /**
125
     * Make a model instance.
126
     *
127
     * @param $ip
128
     *
129
     * @return \Illuminate\Database\Eloquent\Model
130
     */
131
    private function makeModel($ip)
132
    {
133
        return $this->model->newInstance($ip);
134
    }
135
136
    /**
137
     * Read a file contents.
138
     *
139
     * @param $file
140
     *
141
     * @return array
142
     */
143
    private function readFile($file)
144
    {
145
        if ($this->fileSystem->exists($file)) {
0 ignored issues
show
Bug Best Practice introduced by
The property fileSystem does not exist on PragmaRX\Firewall\Repositories\IpList. Did you maybe forget to declare it?
Loading history...
146
            $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
147
148
            return $this->makeArrayOfIps($lines);
149
        }
150
151
        return [];
152
    }
153
154
    /**
155
     * Format all ips in an array.
156
     *
157
     * @param $list
158
     *
159
     * @return array
160
     */
161
    private function formatIpArray($list)
162
    {
163
        return array_map(function ($ip) {
164
            return ['ip_address' => $ip];
165
        }, $this->makeArrayOfIps($list));
166
    }
167
168
    /**
169
     * Make a list of arrays from all sort of things.
170
     *
171
     * @param $list
172
     *
173
     * @return array
174
     */
175
    private function makeArrayOfIps($list)
176
    {
177
        $list = $list ?: [];
178
179
        $ips = [];
180
181
        foreach ($list as $item) {
182
            $ips = array_merge($ips, $this->getIpsFromAnything($item));
183
        }
184
185
        return $ips;
186
    }
187
188
    /**
189
     * Get a list of ips from anything.
190
     *
191
     * @param $item
192
     *
193
     * @return array
194
     */
195
    private function getIpsFromAnything($item)
196
    {
197
        if (starts_with($item, 'country:')) {
198
            return [$item];
199
        }
200
201
        $item = $this->ipAddress()->hostToIp($item);
202
203
        if ($this->ipAddress()->ipV4Valid($item)) {
204
            return [$item];
205
        }
206
207
        return $this->readFile($item);
208
    }
209
210
    /**
211
     * Search for an ip in alist of ips.
212
     *
213
     * @param $ip
214
     * @param $ips
215
     *
216
     * @return null|\Illuminate\Database\Eloquent\Model
217
     */
218
    private function ipArraySearch($ip, $ips)
219
    {
220
        foreach ($ips as $key => $value) {
221
            if (
222
                (isset($value['ip_address']) && $value['ip_address'] == $ip) ||
223
                (strval($key) == $ip) ||
224
                ($value == $ip)
225
            ) {
226
                return $value;
227
            }
228
        }
229
    }
230
231
    /**
232
     * Get all IPs from database.
233
     *
234
     * @return \Illuminate\Support\Collection
235
     */
236
    private function getAllFromDatabase()
237
    {
238
        if ($this->config()->get('use_database')) {
239
            return $this->model->all();
240
        } else {
241
            return collect([]);
242
        }
243
    }
244
245
    /**
246
     * Merge IP lists.
247
     *
248
     * @param $database_ips
249
     * @param $config_ips
250
     *
251
     * @return \Illuminate\Support\Collection
252
     */
253
    private function mergeLists($database_ips, $config_ips)
254
    {
255
        return collect($database_ips)
256
            ->merge(collect($config_ips));
257
    }
258
259
    /**
260
     * Check if an IP address is in a secondary (black/white) list.
261
     *
262
     * @param $ip_address
263
     *
264
     * @return bool|array
265
     */
266
    public function checkSecondaryLists($ip_address)
267
    {
268
        foreach ($this->all() as $range) {
269
            if ($this->ipAddress()->hostToIp($range) == $ip_address || $this->ipAddress()->validRange($ip_address, $range)) {
270
                return $range;
271
            }
272
        }
273
274
        return false;
275
    }
276
277
    /**
278
     * Add an IP to black or whitelist.
279
     *
280
     * @param $whitelist
281
     * @param $ip
282
     * @param bool $force
283
     *
284
     * @return bool
285
     */
286
    public function addToList($whitelist, $ip, $force = false)
287
    {
288
        $list = $whitelist
289
            ? 'whitelist'
290
            : 'blacklist';
291
292
        if (!$this->ipAddress()->isValid($ip)) {
293
            $this->messages()->addMessage(sprintf('%s is not a valid IP address', $ip));
294
295
            return false;
296
        }
297
298
        $listed = $this->whichList($ip);
299
300
        if ($listed == $list) {
301
            $this->messages()->addMessage(sprintf('%s is already %s', $ip, $list.'ed'));
302
303
            return false;
304
        } else {
305
            if (empty($listed) || $force) {
306
                if (!empty($listed)) {
307
                    $this->remove($ip);
308
                }
309
310
                $this->addToProperList($whitelist, $ip);
311
312
                $this->messages()->addMessage(sprintf('%s is now %s', $ip, $list.'ed'));
313
314
                return true;
315
            }
316
        }
317
318
        $this->messages()->addMessage(sprintf('%s is currently %sed', $ip, $listed));
319
320
        return false;
321
    }
322
323
    /**
324
     * Tell in which list (black/white) an IP address is.
325
     *
326
     * @param $ip_address
327
     *
328
     * @return null|string
329
     */
330
    public function whichList($ip_address)
331
    {
332
        if (!$ip_found = $this->find($ip_address)) {
333
            if (!$ip_found = $this->findByCountry($ip_address)) {
334
                if (!$ip_found = $this->checkSecondaryLists($ip_address)) {
335
                    return;
336
                }
337
            }
338
        }
339
340
        return !is_null($ip_found)
341
            ? ($ip_found['whitelisted'] ? 'whitelist' : 'blacklist')
342
            : null;
343
    }
344
345
    /**
346
     * Remove IP from all lists.
347
     *
348
     * @param $ip
349
     *
350
     * @return bool
351
     */
352
    public function remove($ip)
353
    {
354
        $listed = $this->whichList($ip);
355
356
        if (!empty($listed)) {
357
            $this->delete($ip);
358
359
            $this->messages()->addMessage(sprintf('%s removed from %s', $ip, $listed));
360
361
            return true;
362
        }
363
364
        $this->messages()->addMessage(sprintf('%s is not listed', $ip));
365
366
        return false;
367
    }
368
369
    /**
370
     * Add ip or range to array list.
371
     *
372
     * @param $whitelist
373
     * @param $ip
374
     *
375
     * @return array|mixed
376
     */
377
    private function addToArrayList($whitelist, $ip)
378
    {
379
        $data = $this->config()->get($list = $whitelist ? 'whitelist' : 'blacklist');
380
381
        $data[] = $ip;
382
383
        $this->config()->set($list, $data);
384
385
        return $data;
386
    }
387
388
    /**
389
     * Find an IP address in the data source.
390
     *
391
     * @param string $ip
392
     *
393
     * @return mixed
394
     */
395
    public function find($ip)
396
    {
397
        if ($this->cache()->has($ip)) {
398
            return $this->cache()->get($ip);
399
        }
400
401
        if ($model = $this->findIp($ip)) {
402
            $this->cache()->remember($model);
403
        }
404
405
        return $model;
406
    }
407
408
    /**
409
     * Find an IP address by country.
410
     *
411
     * @param $country
412
     *
413
     * @return mixed
414
     */
415
    public function findByCountry($country)
416
    {
417
        if ($this->config()->get('enable_country_search') && !is_null($country = $this->countries()->makeCountryFromString($country))) {
418
            return $this->find($country);
419
        }
420
    }
421
422
    /**
423
     * Find a Ip in the data source.
424
     *
425
     * @param string $ip
426
     *
427
     * @return void
428
     */
429
    public function addToProperList($whitelist, $ip)
430
    {
431
        $this->config()->get('use_database') ?
432
            $this->addToDatabaseList($whitelist, $ip) :
433
            $this->addToArrayList($whitelist, $ip);
434
    }
435
436
    /**
437
     * Delete ip address.
438
     *
439
     * @param $ipAddress
440
     *
441
     * @return bool|void
442
     */
443
    public function delete($ipAddress)
444
    {
445
        $this->config()->get('use_database') ?
446
            $this->removeFromDatabaseList($ipAddress) :
447
            $this->removeFromArrayList($ipAddress);
448
    }
449
450
    /**
451
     * Get all IP addresses.
452
     *
453
     * @return \Illuminate\Support\Collection
454
     */
455
    public function all()
456
    {
457
        $cacheTime = $this->config()->get('ip_list_cache_expire_time');
458
459
        if ($cacheTime > 0 && $list = $this->cache()->get(static::IP_ADDRESS_LIST_CACHE_NAME)) {
0 ignored issues
show
Bug introduced by
The constant PragmaRX\Firewall\Reposi...ADDRESS_LIST_CACHE_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
460
            return $list;
461
        }
462
463
        $list = $this->mergeLists(
464
            $this->getAllFromDatabase(),
465
            $this->toModels($this->getNonDatabaseIps())
466
        );
467
468
        if ($cacheTime > 0) {
469
            $this->cache()->put(
470
                static::IP_ADDRESS_LIST_CACHE_NAME,
471
                $list,
472
                $cacheTime
473
            );
474
        }
475
476
        return $list;
477
    }
478
479
    /**
480
     * Find ip address in all lists.
481
     *
482
     * @param $ip
483
     *
484
     * @return \Illuminate\Database\Eloquent\Model|null|static
485
     */
486
    private function findIp($ip)
487
    {
488
        if ($model = $this->nonDatabaseFind($ip)) {
489
            return $model;
490
        }
491
492
        if ($this->config()->get('use_database')) {
493
            return $this->model->where('ip_address', $ip)->first();
494
        }
495
    }
496
497
    /**
498
     * Find ip in non database lists.
499
     *
500
     * @param $ip
501
     *
502
     * @return \Illuminate\Database\Eloquent\Model
503
     */
504
    private function nonDatabaseFind($ip)
505
    {
506
        $ips = $this->getNonDatabaseIps();
507
508
        if ($ip = $this->ipArraySearch($ip, $ips)) {
509
            return $this->makeModel($ip);
510
        }
511
    }
512
513
    /**
514
     * Add ip or range to database.
515
     *
516
     * @param $whitelist
517
     * @param $ip
518
     *
519
     * @return \Illuminate\Database\Eloquent\Model
520
     */
521
    private function addToDatabaseList($whitelist, $ip)
522
    {
523
        $this->model->unguard();
524
525
        $model = $this->model->create([
526
            'ip_address'  => $ip,
527
            'whitelisted' => $whitelist,
528
        ]);
529
530
        $this->cache()->remember($model);
531
532
        return $model;
533
    }
534
}
535