Report::getComponentsData()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 27
c 1
b 0
f 0
dl 0
loc 37
ccs 26
cts 26
cp 1
rs 9.488
cc 2
nc 2
nop 3
crap 2
1
<?php
2
// phpcs:disable Generic.Files.LineLength
3
/**
4
 * This file is part of the Shieldon package.
5
 *
6
 * (c) Terry L. <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * php version 7.1.0
12
 *
13
 * @category  Web-security
14
 * @package   Shieldon
15
 * @author    Terry Lin <[email protected]>
16
 * @copyright 2019 terrylinooo
17
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
18
 * @link      https://github.com/terrylinooo/shieldon
19
 * @see       https://shieldon.io
20
 */
21
22
declare(strict_types=1);
23
24
namespace Shieldon\Firewall\Panel;
25
26
use Psr\Http\Message\ResponseInterface;
27
use Shieldon\Firewall\Panel\BaseController;
28
use Shieldon\Firewall\Kernel\Enum;
29
use Shieldon\Firewall\Log\ActionLogParsedCache;
30
use ReflectionObject;
31
use function Shieldon\Firewall\__;
32
use function Shieldon\Firewall\get_request;
33
use function array_merge;
34
use function date;
35
36
/**
37
 * The report controller.
38
 */
39
class Report extends BaseController
40
{
41
    /**
42
     *   Public methods       | Desctiotion
43
     *  ----------------------|---------------------------------------------
44
     *   operation            | The page for operating status.
45
     *   actionLog            | The page for displaying action logs.
46
     *  ----------------------|---------------------------------------------
47
     */
48
49
    /**
50 3
     * Constructor
51
     */
52 3
    public function __construct()
53
    {
54
        parent::__construct();
55
    }
56
57
    /**
58
     * Operation status.
59
     *
60 1
     * @return ResponseInterface
61
     */
62 1
    public function operation(): ResponseInterface
63
    {
64 1
        $data = [];
65 1
66 1
        $data = $this->operationTemplateVarsOfComponents($data);
67
        $data = $this->operationTemplateVarsOfFilters($data);
68 1
        $data = $this->operationTemplateVarsOfStatistics($data);
69 1
70 1
        $reasons = [
71 1
            Enum::REASON_MANUAL_BAN_DENIED              => __('panel', 'reason_manual_ban', 'Manually added by administrator'),
72 1
            Enum::REASON_IS_SEARCH_ENGINE_ALLOWED       => __('panel', 'reason_is_search_engine', 'Search engine bot'),
73 1
            Enum::REASON_IS_GOOGLE_ALLOWED              => __('panel', 'reason_is_google', 'Google bot'),
74 1
            Enum::REASON_IS_BING_ALLOWED                => __('panel', 'reason_is_bing', 'Bing bot'),
75 1
            Enum::REASON_IS_YAHOO_ALLOWED               => __('panel', 'reason_is_yahoo', 'Yahoo bot'),
76 1
            Enum::REASON_TOO_MANY_SESSIONS_DENIED       => __('panel', 'reason_too_many_sessions', 'Too many sessions'),
77 1
            Enum::REASON_TOO_MANY_ACCESSE_DENIED        => __('panel', 'reason_too_many_accesses', 'Too many accesses'),
78 1
            Enum::REASON_EMPTY_JS_COOKIE_DENIED         => __('panel', 'reason_empty_js_cookie', 'Unable to create JS cookies'),
79 1
            Enum::REASON_EMPTY_REFERER_DENIED           => __('panel', 'reason_empty_referer', 'Empty referrer'),
80 1
            Enum::REASON_REACH_DAILY_LIMIT_DENIED       => __('panel', 'reason_reached_limit_day', 'Daily limit reached'),
81 1
            Enum::REASON_REACH_HOURLY_LIMIT_DENIED      => __('panel', 'reason_reached_limit_hour', 'Hourly limit reached'),
82 1
            Enum::REASON_REACH_MINUTELY_LIMIT_DENIED    => __('panel', 'reason_reached_limit_minute', 'Minute limit reached'),
83 1
            Enum::REASON_REACH_SECONDLY_LIMIT_DENIED    => __('panel', 'reason_reached_limit_second', 'Second limit reached'),
84 1
            Enum::REASON_INVALID_IP_DENIED              => __('panel', 'reason_invalid_ip', 'Invalid IP address.'),
85 1
            Enum::REASON_DENY_IP_DENIED                 => __('panel', 'reason_deny_ip', 'Denied by IP component.'),
86 1
            Enum::REASON_ALLOW_IP_DENIED                => __('panel', 'reason_allow_ip', 'Allowed by IP component.'),
87 1
            Enum::REASON_COMPONENT_IP_DENIED            => __('panel', 'reason_component_ip', 'Denied by IP component.'),
88 1
            Enum::REASON_COMPONENT_RDNS_DENIED          => __('panel', 'reason_component_rdns', 'Denied by RDNS component.'),
89 1
            Enum::REASON_COMPONENT_HEADER_DENIED        => __('panel', 'reason_component_header', 'Denied by Header component.'),
90 1
            Enum::REASON_COMPONENT_USERAGENT_DENIED     => __('panel', 'reason_component_useragent', 'Denied by User Agent component.'),
91
            Enum::REASON_COMPONENT_TRUSTED_ROBOT_DENIED => __('panel', 'reason_component_trusted_robot', 'Identified as a fake search engine.'),
92 1
        ];
93 1
94 1
        $types = [
95 1
            Enum::ACTION_DENY             => 'DENY',
96 1
            Enum::ACTION_ALLOW            => 'ALLOW',
97
            Enum::ACTION_TEMPORARILY_DENY => 'CAPTCHA',
98 1
        ];
99 1
100
        $data['reason_mapping'] = $reasons;
101 1
        $data['type_mapping'] = $types;
102
103 1
        $data['title'] = __('panel', 'title_operation_status', 'Operation Status');
104
105
        return $this->renderPage('panel/operation_status', $data);
106
    }
107
108
    /**
109
     * Action logs
110
     *
111 2
     * @return ResponseInterface
112
     */
113 2
    public function actionLog(): ResponseInterface
114
    {
115 2
        $getParams = get_request()->getQueryParams();
116
117 2
        $type = $getParams['tab'] ?? 'today';
118 2
119 2
        $validTabs = [
120 2
            'yesterday',
121 2
            'this_month',
122 2
            'last_month',
123 2
            'past_seven_days',
124
            'today',
125 2
        ];
126
127
        if (!in_array($type, $validTabs)) {
128
            // @codeCoverageIgnoreStart
129
            $type = 'today';
130
            // @codeCoverageIgnoreEnd
131 2
        }
132 2
133
        $data = [];
134 2
        $data['last_cached_time'] = '';
135 2
136 2
        if (!empty($this->parser)) {
137
            $result = $this->fetchActionLogsData($type);
138
            $data = array_merge($data, $result);
139 2
        }
140
141 2
        $data['page_availability'] = $this->pageAvailability['logs'];
142
        $data['page_url'] = $this->url('report/actionLog');
143 2
        $data['title'] = __('panel', 'title_action_logs', 'Action Logs');
144
145 2
        return $this->renderPage('panel/action_log_' . $type, $data);
146
    }
147
148
    /**
149
     * Fetch the log data.
150
     *
151
     * @param string $type The date type.
152
     *
153
     * @return array
154
     */
155 2
    private function fetchActionLogsData($type = 'today'): array
156
    {
157 2
        $data = [];
158
159 2
        $logCacheHandler = new ActionLogParsedCache($this->parser->getDirectory());
160
161 2
        $ipDetailsCachedData = $logCacheHandler->get($type);
162
163
        // If we have cached data then we don't need to parse them again.
164
        // This will save a lot of time in parsing logs.
165 2
        if (!empty($ipDetailsCachedData)) {
166
            $data['ip_details'] = $ipDetailsCachedData['ip_details'];
167 1
            $data['period_data'] = $ipDetailsCachedData['period_data'];
168 1
            $data['last_cached_time'] = date('Y-m-d H:i:s', $ipDetailsCachedData['time']);
169 1
170
            if ('today' === $type) {
171 1
                $ipDetailsCachedData = $logCacheHandler->get('past_seven_hours');
172 1
                $data['past_seven_hours'] = $ipDetailsCachedData['period_data'];
173 1
            }
174
        } else {
175
            $this->parser->prepare($type);
176
177
            $data['ip_details'] = $this->parser->getIpData();
178 1
            $data['period_data'] = $this->parser->getParsedPeriodData();
179
180 1
            $logCacheHandler->save($type, $data);
181 1
182
            if ('today' === $type) {
183 1
                $this->parser->prepare('past_seven_hours');
184
                $data['past_seven_hours'] = $this->parser->getParsedPeriodData();
185 1
186 1
                $logCacheHandler->save(
187 1
                    'past_seven_hours',
188
                    [
189 1
                        'period_data' => $data['past_seven_hours'],
190 1
                    ]
191 1
                );
192 1
            }
193 1
        }
194 1
195
        return $data;
196
    }
197
198 2
    /**
199
     * Template variables of the section Components in page Operation.
200
     *
201
     * @param array $data The template varibles.
202
     *
203
     * @return array
204
     */
205
    private function operationTemplateVarsOfComponents(array $data = []): array
206
    {
207
        $data['components'] = [
208 1
            'Ip'         => !empty($this->kernel->component['Ip']),
209
            'TrustedBot' => !empty($this->kernel->component['TrustedBot']),
210 1
            'Header'     => !empty($this->kernel->component['Header']),
211 1
            'Rdns'       => !empty($this->kernel->component['Rdns']),
212 1
            'UserAgent'  => !empty($this->kernel->component['UserAgent']),
213 1
        ];
214 1
215 1
        return $data;
216 1
    }
217
218 1
    /**
219
     * Template variables of the section Filters in the page Operation.
220
     *
221
     * @param array $data The template varibles.
222
     *
223
     * @return array
224
     */
225
    private function operationTemplateVarsOfFilters(array $data = []): array
226
    {
227
        $reflection = new ReflectionObject($this->kernel);
228 1
        $t = $reflection->getProperty('filterStatus');
229
        $t->setAccessible(true);
230 1
        $filterStatus = $t->getValue($this->kernel);
231 1
232 1
        $data['filters'] = $filterStatus;
233 1
234
        return $data;
235 1
    }
236
237 1
    /**
238
     * Template variables of the counters for statistics in the page Operation.
239
     *
240
     * @param array $data The template varibles.
241
     *
242
     * @return array
243
     */
244
    private function operationTemplateVarsOfStatistics(array $data = []): array
245
    {
246
        $ruleList = $this->kernel->driver->getAll('rule');
247 1
248
        $counter = $this->getCounterDefault();
249 1
        $info = $this->getInfoDefault();
250
251 1
        // @codeCoverageIgnoreStart
252 1
        foreach ($ruleList as $ruleInfo) {
253
            $reason = $ruleInfo['reason'];
254
255
            $counter[$reason]++;
256
            $info[$reason][] = $ruleInfo;
257
        }
258
        // @codeCoverageIgnoreEnd
259
260
        $data = $this->getComponentsData($data, $counter, $info);
261
        $data = $this->getFiltersData($data, $counter, $info);
262
263 1
        return $data;
264 1
    }
265
266 1
    /**
267
     * Get filters' data.
268
     *
269
     * @param array $data    The data array.
270
     * @param array $counter The counter array.
271
     * @param array $info    The into array.
272
     *
273
     * @return array
274
     */
275
    private function getFiltersData(array $data, array $counter, array $info): array
276
    {
277
        $filters = ['cookie', 'referer', 'session', 'frequency'];
278 1
279
        foreach ($filters as $v) {
280 1
            $data["filter_$v"] = 0;
281
            $data['rule_list'][$v] = [];
282 1
        }
283 1
284 1
        $a = $counter[Enum::REASON_TOO_MANY_ACCESSE_DENIED];
285
        $b = $counter[Enum::REASON_REACH_DAILY_LIMIT_DENIED];
286
        $c = $counter[Enum::REASON_REACH_HOURLY_LIMIT_DENIED];
287 1
        $d = $counter[Enum::REASON_REACH_MINUTELY_LIMIT_DENIED];
288 1
        $e = $counter[Enum::REASON_REACH_SECONDLY_LIMIT_DENIED];
289 1
        $f = $info[Enum::REASON_DENY_IP_DENIED];
290 1
        $g = $info[Enum::REASON_REACH_DAILY_LIMIT_DENIED];
291 1
        $h = $info[Enum::REASON_REACH_HOURLY_LIMIT_DENIED];
292 1
        $i = $info[Enum::REASON_REACH_MINUTELY_LIMIT_DENIED];
293 1
        $j = $info[Enum::REASON_REACH_SECONDLY_LIMIT_DENIED];
294 1
        $data['filter_frequency'] = $a + $b + $c + $d + $e;
295 1
        $data['rule_list']['frequency'] = array_merge_recursive($f, $g, $h, $i, $j);
296 1
297 1
        $a = $counter[Enum::REASON_EMPTY_REFERER_DENIED];
298 1
        $b = $info[Enum::REASON_EMPTY_REFERER_DENIED];
299
        $data['filter_referer'] = $a;
300 1
        $data['rule_list']['referer'] = $b;
301 1
302 1
        $a = $counter[Enum::REASON_EMPTY_JS_COOKIE_DENIED];
303 1
        $b = $info[Enum::REASON_EMPTY_JS_COOKIE_DENIED];
304
        $data['filter_cookie'] = $a;
305 1
        $data['rule_list']['cookie'] = $b;
306 1
307 1
        $a = $counter[Enum::REASON_TOO_MANY_SESSIONS_DENIED];
308 1
        $b = $info[Enum::REASON_TOO_MANY_SESSIONS_DENIED];
309
        $data['filter_session'] = $a;
310 1
        $data['rule_list']['session'] = $b;
311 1
312 1
        return $data;
313 1
    }
314
315 1
    /**
316
     * Get components' data.
317
     *
318
     * @param array $data    The data array.
319
     * @param array $counter The counter array.
320
     * @param array $info    The into array.
321
     *
322
     * @return array
323
     */
324
    private function getComponentsData(array $data, array $counter, array $info): array
325
    {
326
        $components = ['ip', 'rdns', 'header', 'useragent', 'trustedbot'];
327 1
328
        foreach ($components as $v) {
329 1
            $data["component_$v"] = 0;
330
            $data['rule_list'][$v] = [];
331 1
        }
332 1
333 1
        $a = $counter[Enum::REASON_DENY_IP_DENIED];
334
        $b = $counter[Enum::REASON_COMPONENT_IP_DENIED];
335
        $c = $info[Enum::REASON_DENY_IP_DENIED];
336 1
        $d = $info[Enum::REASON_COMPONENT_IP_DENIED];
337 1
        $data['component_ip'] = $a + $b;
338 1
        $data['rule_list']['ip'] = array_merge_recursive($c, $d);
339 1
340 1
        $a = $counter[Enum::REASON_COMPONENT_RDNS_DENIED];
341 1
        $b = $info[Enum::REASON_COMPONENT_RDNS_DENIED];
342
        $data['component_rdns'] = $a;
343 1
        $data['rule_list']['rdns'] = $b;
344 1
345 1
        $a = $counter[Enum::REASON_COMPONENT_HEADER_DENIED];
346 1
        $b = $info[Enum::REASON_COMPONENT_HEADER_DENIED];
347
        $data['component_header'] = $a;
348 1
        $data['rule_list']['header'] = $b;
349 1
350 1
        $a = $counter[Enum::REASON_COMPONENT_USERAGENT_DENIED];
351 1
        $b = $info[Enum::REASON_COMPONENT_USERAGENT_DENIED];
352
        $data['component_useragent'] = $a;
353 1
        $data['rule_list']['useragent'] = $b;
354 1
355 1
        $a = $counter[Enum::REASON_COMPONENT_TRUSTED_ROBOT_DENIED];
356 1
        $b = $info[Enum::REASON_COMPONENT_TRUSTED_ROBOT_DENIED];
357
        $data['component_trustedbot'] = $a;
358 1
        $data['rule_list']['trustedbot'] = $b;
359 1
360 1
        return $data;
361 1
    }
362
363 1
    /**
364
     * Get counter default.
365
     *
366
     * @return array
367
     */
368
    private function getCounterDefault(): array
369
    {
370
        $counter = [];
371 1
372
        $counter[Enum::REASON_DENY_IP_DENIED]                 = 0;
373 1
        $counter[Enum::REASON_COMPONENT_IP_DENIED]            = 0;
374
        $counter[Enum::REASON_COMPONENT_RDNS_DENIED]          = 0;
375 1
        $counter[Enum::REASON_COMPONENT_HEADER_DENIED]        = 0;
376 1
        $counter[Enum::REASON_COMPONENT_USERAGENT_DENIED]     = 0;
377 1
        $counter[Enum::REASON_COMPONENT_TRUSTED_ROBOT_DENIED] = 0;
378 1
        $counter[Enum::REASON_TOO_MANY_ACCESSE_DENIED]       = 0;
379 1
        $counter[Enum::REASON_REACH_DAILY_LIMIT_DENIED]       = 0;
380 1
        $counter[Enum::REASON_REACH_HOURLY_LIMIT_DENIED]      = 0;
381 1
        $counter[Enum::REASON_REACH_MINUTELY_LIMIT_DENIED]    = 0;
382 1
        $counter[Enum::REASON_REACH_SECONDLY_LIMIT_DENIED]    = 0;
383 1
        $counter[Enum::REASON_EMPTY_REFERER_DENIED]           = 0;
384 1
        $counter[Enum::REASON_EMPTY_JS_COOKIE_DENIED]         = 0;
385 1
        $counter[Enum::REASON_TOO_MANY_SESSIONS_DENIED]       = 0;
386 1
387 1
        return $counter;
388 1
    }
389
390 1
    /**
391
     * Get info default.
392
     *
393
     * @return array
394
     */
395
    private function getInfoDefault(): array
396
    {
397
        $info = [];
398 1
399
        $info[Enum::REASON_DENY_IP_DENIED]                 = [];
400 1
        $info[Enum::REASON_COMPONENT_IP_DENIED]            = [];
401
        $info[Enum::REASON_COMPONENT_RDNS_DENIED]          = [];
402 1
        $info[Enum::REASON_COMPONENT_HEADER_DENIED]        = [];
403 1
        $info[Enum::REASON_COMPONENT_USERAGENT_DENIED]     = [];
404 1
        $info[Enum::REASON_COMPONENT_TRUSTED_ROBOT_DENIED] = [];
405 1
        $info[Enum::REASON_DENY_IP_DENIED]                 = [];
406 1
        $info[Enum::REASON_REACH_DAILY_LIMIT_DENIED]       = [];
407 1
        $info[Enum::REASON_REACH_HOURLY_LIMIT_DENIED]      = [];
408 1
        $info[Enum::REASON_REACH_MINUTELY_LIMIT_DENIED]    = [];
409 1
        $info[Enum::REASON_REACH_SECONDLY_LIMIT_DENIED]    = [];
410 1
        $info[Enum::REASON_EMPTY_REFERER_DENIED]           = [];
411 1
        $info[Enum::REASON_EMPTY_JS_COOKIE_DENIED]         = [];
412 1
        $info[Enum::REASON_TOO_MANY_SESSIONS_DENIED]       = [];
413 1
414 1
        return $info;
415 1
    }
416
}
417