Passed
Push — 2.x ( 958536...e12ed9 )
by Terry
02:44 queued 25s
created

MainTrait::setIpSource()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 22
rs 10
1
<?php
2
/*
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\Firewall\Firewall;
14
15
use Shieldon\Firewall\Firewall\Driver\DriverFactory;
16
use Shieldon\Firewall\Firewall\Captcha\CaptchaFactory;
17
18
/*
19
 * Main Trait for Firwall class.
20
 */
21
trait MainTrait
22
{
23
    /**
24
     * Fetch value from configuration.
25
     *
26
     * @param string $option
27
     * @param string $section
28
     *
29
     * @return mixed
30
     */
31
    abstract function getOption(string $option, string $section = '');
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
32
33
    /**
34
     * Set a data driver for the use of Shiedon Firewall.
35
     * Currently supports File, Redis, MySQL and SQLite.
36
     *
37
     * @return void
38
     */
39
    protected function setDriver(): void
40
    {
41
        $driverType = $this->getOption('driver_type');
42
        $driverSetting = $this->getOption($driverType, 'drivers');
43
44
        if (isset($driverSetting['directory_path'])) {
45
            $driverSetting['directory_path'] = $driverSetting['directory_path'] ?: $this->directory;
46
        }
47
48
        $driverInstance = DriverFactory::getInstance($driverType, $driverSetting);
49
50
        $this->status = false;
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
51
        if ($driverInstance !== null) {
52
            $this->kernel->add($driverInstance);
53
            $this->status = true;
54
        }
55
    }
56
57
    /**
58
     * Filters
59
     *
60
     * (1) Session.
61
     * (2) Cookie generated by JavaScript code.
62
     * (3) HTTP referrer information.
63
     * (4) Pageview frequency.
64
     *
65
     * @return void
66
     */
67
    protected function setFilters(): void
68
    {
69
        $sessionSetting   = $this->getOption('session', 'filters');
70
        $cookieSetting    = $this->getOption('cookie', 'filters');
71
        $refererSetting   = $this->getOption('referer', 'filters');
72
        $frequencySetting = $this->getOption('frequency', 'filters');
73
74
        $filterConfig = [
75
            'session'   => $sessionSetting['enable'],
76
            'cookie'    => $cookieSetting['enable'],
77
            'referer'   => $refererSetting['enable'],
78
            'frequency' => $frequencySetting['enable'],
79
        ];
80
81
        $this->kernel->setFilters($filterConfig);
82
83
        $this->kernel->setProperty('limit_unusual_behavior', [
84
            'session' => $sessionSetting['config']['quota'] ?? 5,
85
            'cookie'  => $cookieSetting['config']['quota'] ?? 5,
86
            'referer' => $refererSetting['config']['quota'] ?? 5,
87
        ]);
88
89
        // if ($frequencySetting['enable']) {
90
        $frequencyQuota = [
91
            's' => $frequencySetting['config']['quota_s'] ?? 2,
92
            'm' => $frequencySetting['config']['quota_m'] ?? 10,
93
            'h' => $frequencySetting['config']['quota_h'] ?? 30,
94
            'd' => $frequencySetting['config']['quota_d'] ?? 60,
95
        ];
96
97
        $this->kernel->setProperty('time_unit_quota', $frequencyQuota);
98
99
        // if ($cookieSetting['enable']) {
100
        $cookieName = $cookieSetting['config']['cookie_name'] ?? 'ssjd';
101
        $cookieDomain = $cookieSetting['config']['cookie_domain'] ?? '';
102
        $cookieValue = $cookieSetting['config']['cookie_value'] ?? '1';
103
104
        $this->kernel->setProperty('cookie_name', $cookieName);
105
        $this->kernel->setProperty('cookie_domain', $cookieDomain);
106
        $this->kernel->setProperty('cookie_value', $cookieValue);
107
108
        // if ($refererSetting['enable']) {
109
        $this->kernel->setProperty('interval_check_referer', $refererSetting['config']['time_buffer']);
110
111
        // if ($sessionSetting['enable']) {
112
        $this->kernel->setProperty('interval_check_session', $sessionSetting['config']['time_buffer']);
113
    }
114
115
    /**
116
     * Components
117
     * 
118
     * (1) Ip
119
     * (2) Rdns
120
     * (3) Header
121
     * (4) User-agent
122
     * (5) Trusted bot
123
     *
124
     * @return void
125
     */
126
    protected function setComponents(): void
127
    {
128
        $componentConfig = [
129
            'Ip'         => $this->getOption('ip', 'components'),
130
            'Rdns'       => $this->getOption('rdns', 'components'),
131
            'Header'     => $this->getOption('header', 'components'),
132
            'UserAgent'  => $this->getOption('user_agent', 'components'),
133
            'TrustedBot' => $this->getOption('trusted_bot', 'components'),
134
        ];
135
136
        foreach ($componentConfig as $className => $config) {
137
            $class = 'Shieldon\Firewall\Component\\' . $className;
138
139
            if ($config['enable']) {
140
                $componentInstance = new $class();
141
142
                if ($className === 'Ip') {
143
                    $this->kernel->add($componentInstance);
144
145
                    // Need Ip component to be loaded before calling this method.
146
                    $this->applyComponentIpManager();
147
                    
148
                } elseif ($config['strict_mode']) {
149
                    $componentInstance->setStrict(true);
150
                    $this->kernel->add($componentInstance);
151
                }
152
            }
153
        }
154
    }
155
156
    /**
157
     * Captcha modules.
158
     * 
159
     * (1) Google ReCaptcha
160
     * (2) Simple image captcha.
161
     *
162
     * @return void
163
     */
164
    protected function setCaptchas(): void
165
    {
166
        $captchaList = [
167
            'recaptcha',
168
            'image',
169
        ];
170
171
        foreach ($captchaList as $captcha) {
172
            $setting = $this->getOption($captcha, 'captcha_modules');
173
174
            if (is_array($setting)) {
175
176
                // Initialize messenger instances from the factory/
177
                if (CaptchaFactory::check($captcha, $setting)) {
178
    
179
                    $this->kernel->add(
180
                        CaptchaFactory::getInstance(
181
                            // The ID of the captcha module in the configuration.
182
                            $captcha, 
183
                            // The settings of the captcha module in the configuration.
184
                            $setting    
185
                        )
186
                    );
187
                }
188
            }
189
190
            unset($setting);
191
        }
192
    }
193
194
    /**
195
     * Set up the action logger.
196
     *
197
     * @return void
198
     */
199
    protected function setLogger(): void
200
    {
201
        $loggerSetting = $this->getOption('action', 'loggers');
202
203
        if ($loggerSetting['enable']) {
204
            if (!empty($loggerSetting['config']['directory_path'])) {
205
                $this->kernel->add(new ActionLogger($loggerSetting['config']['directory_path']));
0 ignored issues
show
Bug introduced by
The type Shieldon\Firewall\Firewall\ActionLogger was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
206
            }
207
        }
208
    }
209
210
    /**
211
     * Apply the denied list and the allowed list to Ip Component.
212
     */
213
    protected function applyComponentIpManager()
214
    {
215
        $ipList = $this->getOption('ip_manager');
216
217
        $allowedList = [];
218
        $deniedList = [];
219
220
        if (is_array($ipList)) {
221
            foreach ($ipList as $ip) {
222
223
                if (0 === strpos($this->kernel->getCurrentUrl(), $ip['url']) ) {
224
    
225
                    if ('allow' === $ip['rule']) {
226
                        $allowedList[] = $ip['ip'];
227
                    }
228
    
229
                    if ('deny' === $ip['rule']) {
230
                        $deniedList[] = $ip['ip'];
231
                    }
232
                }
233
            }
234
        }
235
236
        $this->kernel->component['Ip']->setAllowedItems($allowedList);
237
        $this->kernel->component['Ip']->setDeniedItems($deniedList);
238
    }
239
240
/**
241
     * Set the channel ID.
242
     *
243
     * @return void
244
     */
245
    protected function setChannel(): void
246
    {
247
        $channelId = $this->getOption('channel_id');
248
249
        if ($channelId) {
250
            $this->kernel->setChannel($channelId);
251
        }
252
    }
253
254
    /**
255
     * If you use CDN, please choose the real IP source.
256
     *
257
     * @return void
258
     */
259
    protected function setIpSource(): void
260
    {
261
        $ipSourceType = $this->getOption('ip_variable_source');
262
        $serverParams = get_request()->getServerParams();
0 ignored issues
show
Bug introduced by
The function get_request was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

262
        $serverParams = /** @scrutinizer ignore-call */ get_request()->getServerParams();
Loading history...
263
264
        /**
265
         * REMOTE_ADDR: general
266
         * HTTP_CF_CONNECTING_IP: Cloudflare
267
         * HTTP_X_FORWARDED_FOR: Google Cloud CDN, Google Load-balancer, AWS.
268
         * HTTP_X_FORWARDED_HOST: KeyCDN, or other CDN providers not listed here.
269
         * 
270
         */
271
        $key = array_search(true, $ipSourceType);
272
        $ip = $serverParams[$key];
273
274
        if (empty($ip)) {
275
            // @codeCoverageIgnoreStart
276
            throw new RuntimeException('IP source is not set correctly.');
0 ignored issues
show
Bug introduced by
The type Shieldon\Firewall\Firewall\RuntimeException was not found. Did you mean RuntimeException? If so, make sure to prefix the type with \.
Loading history...
277
            // @codeCoverageIgnoreEnd
278
        }
279
280
        $this->kernel->setIp($ip);
281
    }
282
283
    /**
284
     * Set deny attempts.
285
     *
286
     * @return void
287
     */
288
    protected function setDenyTooManyAttempts(): void
289
    {
290
        $setting = $this->getOption('failed_attempts_in_a_row', 'events');
291
292
        $enableDataCircle     = $setting['data_circle']['enable']     ?: false;
293
        $enableSystemFirewall = $setting['system_firewall']['enable'] ?: false;
294
295
        $this->kernel->setProperty('deny_attempt_enable', [
296
            'data_circle'     => $enableDataCircle,
297
            'system_firewall' => $enableSystemFirewall,
298
        ]);
299
300
        $this->kernel->setProperty('deny_attempt_buffer', [
301
            'data_circle'     => $setting['data_circle']['buffer'] ?? 10,
302
            'system_firewall' => $setting['data_circle']['buffer'] ?? 10,
303
        ]);
304
305
        // Check the time of the last failed attempt. @since 0.2.0
306
        $recordAttempt = $this->getOption('record_attempt');
307
308
        $detectionPeriod = $recordAttempt['detection_period'] ?? 5;
309
        $timeToReset     = $recordAttempt['time_to_reset']    ?? 1800;
310
311
        $this->kernel->setProperty('record_attempt_detection_period', $detectionPeriod);
312
        $this->kernel->setProperty('reset_attempt_counter', $timeToReset);
313
    }
314
315
    /**
316
     * Set iptables working folder.
317
     *
318
     * @return void
319
     */
320
    protected function setIptablesBridgeDirectory(): void
321
    {
322
        $iptablesSetting = $this->getOption('config', 'iptables');
323
        $this->kernel->setProperty('iptables_watching_folder',  $iptablesSetting['watching_folder']);
324
    }
325
326
    /**
327
     * Set the online session limit.
328
     *
329
     * @return void
330
     */
331
    protected function setSessionLimit(): void
332
    {
333
        $sessionLimitSetting = $this->getOption('online_session_limit');
334
335
        if ($sessionLimitSetting['enable']) {
336
337
            $onlineUsers = $sessionLimitSetting['config']['count']  ?? 100;
338
            $alivePeriod = $sessionLimitSetting['config']['period'] ?? 300;
339
340
            $this->kernel->limitSession($onlineUsers, $alivePeriod);
341
        }
342
    }
343
344
    /**
345
     * Set the cron job.
346
     * This is triggered by the pageviews, not system cron job.
347
     *
348
     * @return void
349
     */
350
    protected function setCronJob(): void 
351
    {
352
        if (!$this->status) {
353
            return;
354
        }
355
356
        $cronjobSetting = $this->getOption('reset_circle', 'cronjob');
357
358
        if ($cronjobSetting['enable']) {
359
360
            $nowTime = time();
361
362
            $lastResetTime = $cronjobSetting['config']['last_update'];
363
364
            if (!empty($lastResetTime) ) {
365
                $lastResetTime = strtotime($lastResetTime);
366
            } else {
367
                // @codeCoverageIgnoreStart
368
                $lastResetTime = strtotime(date('Y-m-d 00:00:00'));
369
                // @codeCoverageIgnoreEnd
370
            }
371
372
            if (($nowTime - $lastResetTime) > $cronjobSetting['config']['period']) {
373
374
                $updateResetTime = date('Y-m-d 00:00:00');
375
376
                // Update new reset time.
377
                $this->setConfig('cronjob.reset_circle.config.last_update', $updateResetTime);
0 ignored issues
show
Bug introduced by
It seems like setConfig() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

377
                $this->/** @scrutinizer ignore-call */ 
378
                       setConfig('cronjob.reset_circle.config.last_update', $updateResetTime);
Loading history...
378
                $this->updateConfig();
0 ignored issues
show
Bug introduced by
It seems like updateConfig() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

378
                $this->/** @scrutinizer ignore-call */ 
379
                       updateConfig();
Loading history...
379
380
                // Remove all logs.
381
                $this->kernel->driver->rebuild();
382
            }
383
        }
384
    }
385
386
    /**
387
     * Set the URLs that want to be excluded from Shieldon protection.
388
     *
389
     * @return void
390
     */
391
    protected function setExcludedUrls(): void
392
    {
393
        $excludedUrls = $this->getOption('excluded_urls');
394
395
        if (!empty($excludedUrls)) {
396
            $list = array_column($excludedUrls, 'url');
397
398
            $this->kernel->setExcludedUrls($list);
399
        }
400
    }
401
402
    /**
403
     * WWW-Athentication.
404
     *
405
     * @return void
406
     */
407
    protected function setPageAuthentication(): void
408
    {
409
        $authenticateList = $this->getOption('www_authenticate');
410
411
        if (is_array($authenticateList)) {
412
            $this->add(new Middleware\httpAuthentication($authenticateList));
0 ignored issues
show
Bug introduced by
It seems like add() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

412
            $this->/** @scrutinizer ignore-call */ 
413
                   add(new Middleware\httpAuthentication($authenticateList));
Loading history...
Bug introduced by
The type Shieldon\Firewall\Firewa...ware\httpAuthentication was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
413
        }
414
    }
415
416
    /**
417
     * Set dialog UI.
418
     *
419
     * @return void
420
     */
421
    protected function setDialogUserInterface()
422
    {
423
        $ui = $this->getOption('dialog_ui');
424
425
        if (!empty($ui)) {
426
            get_session()->set('shieldon_ui_lang', $ui['lang']);
0 ignored issues
show
Bug introduced by
The function get_session was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

426
            /** @scrutinizer ignore-call */ 
427
            get_session()->set('shieldon_ui_lang', $ui['lang']);
Loading history...
427
            $this->kernel->setDialogUI($this->getOption('dialog_ui'));
428
        }
429
    }
430
}
431