1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <contact@terryl.in>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 * 
10
 * php version 7.1.0
11
 * 
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <contact@terryl.in>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20

21
declare(strict_types=1);
22

23
namespace Shieldon\Firewall\Panel;
24

25
use Psr\Http\Message\ResponseInterface;
26
use Shieldon\Firewall\Panel\BaseController;
27
use Shieldon\Firewall\Log\ActionLogParsedCache;
28
use ReflectionObject;
29
use function Shieldon\Firewall\__;
30
use function Shieldon\Firewall\get_request;
31
use function array_merge;
32
use function date;
33

34
/**
35
 * The report controller.
36
 */
37
class Report extends BaseController
38
{
39
    /**
40
     *   Public methods       | Desctiotion
41
     *  ----------------------|---------------------------------------------
42
     *   operation            | The page for operating status.
43
     *   actionLog            | The page for displaying action logs.
44
     *  ----------------------|---------------------------------------------
45
     */
46

47
    /**
48
     * Constructor
49
     */
50 3
    public function __construct() 
51
    {
52 3
        parent::__construct();
53
    }
54

55
    /**
56
     * Operation status.
57
     *
58
     * @return ResponseInterface
59
     */
60 3
    public function operation(): ResponseInterface
61
    {
62 3
        $data = [];
63

64 3
        $data = $this->operationTemplateVarsOfComponents($data);
65 3
        $data = $this->operationTemplateVarsOfFilters($data);
66 3
        $data = $this->operationTemplateVarsOfStatistics($data);
67

68
        $reasons = [
69 3
            $this->kernel::REASON_MANUAL_BAN              => __('panel', 'reason_manual_ban', 'Added manually by administrator'),
70 3
            $this->kernel::REASON_IS_SEARCH_ENGINE        => __('panel', 'reason_is_search_engine', 'Search engine bot'),
71 3
            $this->kernel::REASON_IS_GOOGLE               => __('panel', 'reason_is_google', 'Google bot'),
72 3
            $this->kernel::REASON_IS_BING                 => __('panel', 'reason_is_bing', 'Bing bot'),
73 3
            $this->kernel::REASON_IS_YAHOO                => __('panel', 'reason_is_yahoo', 'Yahoo bot'),
74 3
            $this->kernel::REASON_TOO_MANY_SESSIONS       => __('panel', 'reason_too_many_sessions', 'Too many sessions'),
75 3
            $this->kernel::REASON_TOO_MANY_ACCESSES       => __('panel', 'reason_too_many_accesses', 'Too many accesses'),
76 3
            $this->kernel::REASON_EMPTY_JS_COOKIE         => __('panel', 'reason_empty_js_cookie', 'Cannot create JS cookies'),
77 3
            $this->kernel::REASON_EMPTY_REFERER           => __('panel', 'reason_empty_referer', 'Empty referrer'),
78 3
            $this->kernel::REASON_REACHED_LIMIT_DAY       => __('panel', 'reason_reached_limit_day', 'Limit per day reached'),
79 3
            $this->kernel::REASON_REACHED_LIMIT_HOUR      => __('panel', 'reason_reached_limit_hour', 'Limit per hour reached'),
80 3
            $this->kernel::REASON_REACHED_LIMIT_MINUTE    => __('panel', 'reason_reached_limit_minute', 'Limit per minute reached'),
81 3
            $this->kernel::REASON_REACHED_LIMIT_SECOND    => __('panel', 'reason_reached_limit_second', 'Limit per second reached'),
82 3
            $this->kernel::REASON_INVALID_IP              => __('panel', 'reason_invalid_ip', 'Invalid IP address.'),
83 3
            $this->kernel::REASON_DENY_IP                 => __('panel', 'reason_deny_ip', 'Denied by IP component.'),
84 3
            $this->kernel::REASON_ALLOW_IP                => __('panel', 'reason_allow_ip', 'Allowed by IP component.'),
85 3
            $this->kernel::REASON_COMPONENT_IP            => __('panel', 'reason_component_ip', 'Denied by IP component.'),
86 3
            $this->kernel::REASON_COMPONENT_RDNS          => __('panel', 'reason_component_rdns', 'Denied by RDNS component.'),
87 3
            $this->kernel::REASON_COMPONENT_HEADER        => __('panel', 'reason_component_header', 'Denied by Header component.'),
88 3
            $this->kernel::REASON_COMPONENT_USERAGENT     => __('panel', 'reason_component_useragent', 'Denied by User-agent component.'),
89 3
            $this->kernel::REASON_COMPONENT_TRUSTED_ROBOT => __('panel', 'reason_component_trusted_robot', 'Identified as a fake search engine.'),
90
        ];
91

92
        $types = [
93 3
            $this->kernel::ACTION_DENY             => 'DENY',
94 3
            $this->kernel::ACTION_ALLOW            => 'ALLOW',
95 3
            $this->kernel::ACTION_TEMPORARILY_DENY => 'CAPTCHA',
96
        ];
97

98 3
        $data['reason_mapping'] = $reasons;
99 3
        $data['type_mapping'] = $types;
100

101 3
        $data['title'] = __('panel', 'title_operation_status', 'Operation Status');
102

103 3
        return $this->renderPage('panel/operation_status', $data);
104
    }
105

106
    /**
107
     * Action logs
108
     *
109
     * @return ResponseInterface
110
     */
111 3
    public function actionLog(): ResponseInterface
112
    {
113 3
        $getParams = get_request()->getQueryParams();
114

115 3
        $type = $getParams['tab'] ?? 'today';
116

117
        $validTabs = [
118 3
            'yesterday',
119
            'this_month',
120
            'last_month',
121
            'past_seven_days',
122
            'today',
123
        ];
124

125 3
        if (!in_array($type, $validTabs)) {
126
            // @codeCoverageIgnoreStart
127
            $type = 'today';
128
            // @codeCoverageIgnoreEnd
129
        }
130

131 3
        $data = [];
132 3
        $data['last_cached_time'] = '';
133

134 3
        if (!empty($this->parser)) {
135 3
            $result = $this->fetchActionLogsData($type);
136 3
            $data = array_merge($data, $result);
137
        }
138

139 3
        $data['page_availability'] = $this->pageAvailability['logs'];;
140

141 3
        $data['page_url'] = $this->url('report/actionLog');
142

143 3
        $data['title'] = __('panel', 'title_action_logs', 'Action Logs');
144

145 3
        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 3
    private function fetchActionLogsData($type = 'today'): array
156
    {
157 3
        $data = [];
158

159 3
        $logCacheHandler = new ActionLogParsedCache($this->parser->getDirectory());
160

161 3
        $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 3
        if (!empty($ipDetailsCachedData)) {
166

167 3
            $data['ip_details'] = $ipDetailsCachedData['ip_details'];
168 3
            $data['period_data'] = $ipDetailsCachedData['period_data'];
169 3
            $data['last_cached_time'] = date('Y-m-d H:i:s', $ipDetailsCachedData['time']);
170

171 3
            if ('today' === $type) {
172 3
                $ipDetailsCachedData = $logCacheHandler->get('past_seven_hours');
173 3
                $data['past_seven_hours'] = $ipDetailsCachedData['period_data'];
174
            }
175

176
        } else {
177

178 3
            $this->parser->prepare($type);
179

180 3
            $data['ip_details'] = $this->parser->getIpData();
181 3
            $data['period_data'] = $this->parser->getParsedPeriodData();
182

183 3
            $logCacheHandler->save($type, $data);
184

185 3
            if ('today' === $type) {
186 3
                $this->parser->prepare('past_seven_hours');
187 3
                $data['past_seven_hours'] = $this->parser->getParsedPeriodData();
188

189 3
                $logCacheHandler->save(
190 3
                    'past_seven_hours',
191
                    [
192 3
                        'period_data' => $data['past_seven_hours']
193
                    ]
194
                );
195
            }
196
        }
197

198 3
        return $data;
199
    }
200

201
    /**
202
     * Template variables of the section Components in page Operation.
203
     *
204
     * @param array $data The template varibles.
205
     *
206
     * @return array
207
     */
208 3
    private function operationTemplateVarsOfComponents(array $data = []): array
209
    {
210 3
        $data['components'] = [
211 3
            'Ip'         => (!empty($this->kernel->component['Ip']))         ? true : false,
212 3
            'TrustedBot' => (!empty($this->kernel->component['TrustedBot'])) ? true : false,
213 3
            'Header'     => (!empty($this->kernel->component['Header']))     ? true : false,
214 3
            'Rdns'       => (!empty($this->kernel->component['Rdns']))       ? true : false,
215 3
            'UserAgent'  => (!empty($this->kernel->component['UserAgent']))  ? true : false,
216
        ];
217

218 3
        return $data;
219
    }
220

221
    /**
222
     * Template variables of the section Filters in the page Operation.
223
     *
224
     * @param array $data The template varibles.
225
     *
226
     * @return array
227
     */
228 3
    private function operationTemplateVarsOfFilters(array $data = []): array
229
    {
230 3
        $reflection = new ReflectionObject($this->kernel);
231 3
        $t = $reflection->getProperty('filterStatus');
232 3
        $t->setAccessible(true);
233 3
        $filterStatus = $t->getValue($this->kernel);
234

235 3
        $data['filters'] = $filterStatus;
236

237 3
        return $data;
238
    }
239

240
    /**
241
     * Template variables of the counters for statistics in the page Operation.
242
     *
243
     * @param array $data The template varibles.
244
     *
245
     * @return array
246
     */
247 3
    private function operationTemplateVarsOfStatistics(array $data = []): array
248
    {
249 3
        $ruleList = $this->kernel->driver->getAll('rule');
250

251 3
        $counter = $this->getCounterDefault();
252 3
        $info = $this->getInfoDefault();
253

254
        // @codeCoverageIgnoreStart
255
        foreach ($ruleList as $ruleInfo) {
256
            $reason = $ruleInfo['reason'];
257

258
            $counter[$reason]++;
259
            $info[$reason][] = $ruleInfo;
260
        }
261
        // @codeCoverageIgnoreEnd
262

263 3
        $data = $this->getComponentsData($data, $counter, $info);
264 3
        $data = $this->getFiltersData($data, $counter, $info);
265

266 3
        return $data;
267
    }
268

269
    /**
270
     * Get filters' data.
271
     *
272
     * @param array $data    The data array.
273
     * @param array $counter The counter array.
274
     * @param array $info    The into array.
275
     *
276
     * @return array
277
     */
278 3
    private function getFiltersData(array $data, array $counter, array $info): array
279
    {
280 3
        $filters = ['cookie', 'referer', 'session', 'frequency'];
281

282 3
        foreach ($filters as $v) {
283 3
            $data["filter_$v"] = 0;
284 3
            $data['rule_list'][$v] = [];
285
        }
286

287 3
        $a = $counter[$this->kernel::REASON_TOO_MANY_ACCESSES];
288 3
        $b = $counter[$this->kernel::REASON_REACHED_LIMIT_DAY];
289 3
        $c = $counter[$this->kernel::REASON_REACHED_LIMIT_HOUR];
290 3
        $d = $counter[$this->kernel::REASON_REACHED_LIMIT_MINUTE];
291 3
        $e = $counter[$this->kernel::REASON_REACHED_LIMIT_SECOND];
292 3
        $f = $info[$this->kernel::REASON_DENY_IP];
293 3
        $g = $info[$this->kernel::REASON_REACHED_LIMIT_DAY];
294 3
        $h = $info[$this->kernel::REASON_REACHED_LIMIT_HOUR];
295 3
        $i = $info[$this->kernel::REASON_REACHED_LIMIT_MINUTE];
296 3
        $j = $info[$this->kernel::REASON_REACHED_LIMIT_SECOND];
297 3
        $data['filter_frequency'] = $a + $b + $c + $d + $e;
298 3
        $data['rule_list']['frequency'] = array_merge_recursive($f, $g, $h, $i, $j);
299

300 3
        $a = $counter[$this->kernel::REASON_EMPTY_REFERER];
301 3
        $b = $info[$this->kernel::REASON_EMPTY_REFERER];
302 3
        $data['filter_referer'] = $a;
303 3
        $data['rule_list']['referer'] = $b;
304

305 3
        $a = $counter[$this->kernel::REASON_EMPTY_JS_COOKIE];
306 3
        $b = $info[$this->kernel::REASON_EMPTY_JS_COOKIE];
307 3
        $data['filter_cookie'] = $a;
308 3
        $data['rule_list']['cookie'] = $b;
309

310 3
        $a = $counter[$this->kernel::REASON_TOO_MANY_SESSIONS];
311 3
        $b = $info[$this->kernel::REASON_TOO_MANY_SESSIONS];
312 3
        $data['filter_session'] = $a;
313 3
        $data['rule_list']['session'] = $b;
314

315 3
        return $data;
316
    }
317

318
    /**
319
     * Get components' data.
320
     * 
321
     * @param array $data    The data array.
322
     * @param array $counter The counter array.
323
     * @param array $info    The into array.
324
     *
325
     * @return array
326
     */
327 3
    private function getComponentsData(array $data, array $counter, array $info): array
328
    {
329 3
        $components = ['ip', 'rdns', 'header', 'useragent', 'trustedbot'];
330

331 3
        foreach ($components as $v) {
332 3
            $data["component_$v"] = 0;
333 3
            $data['rule_list'][$v] = [];
334
        }
335

336 3
        $a = $counter[$this->kernel::REASON_DENY_IP];
337 3
        $b = $counter[$this->kernel::REASON_COMPONENT_IP];
338 3
        $c = $info[$this->kernel::REASON_DENY_IP];
339 3
        $d = $info[$this->kernel::REASON_COMPONENT_IP];
340 3
        $data['component_ip'] = $a + $b;
341 3
        $data['rule_list']['ip'] = array_merge_recursive($c, $d);
342

343 3
        $a = $counter[$this->kernel::REASON_COMPONENT_RDNS];
344 3
        $b = $info[$this->kernel::REASON_COMPONENT_RDNS];
345 3
        $data['component_rdns'] = $a;
346 3
        $data['rule_list']['rdns'] = $b;
347

348 3
        $a = $counter[$this->kernel::REASON_COMPONENT_HEADER];
349 3
        $b = $info[$this->kernel::REASON_COMPONENT_HEADER];
350 3
        $data['component_header'] = $a;
351 3
        $data['rule_list']['header'] = $b;
352

353 3
        $a = $counter[$this->kernel::REASON_COMPONENT_USERAGENT];
354 3
        $b = $info[$this->kernel::REASON_COMPONENT_USERAGENT];
355 3
        $data['component_useragent'] = $a;
356 3
        $data['rule_list']['useragent'] = $b;
357

358 3
        $a = $counter[$this->kernel::REASON_COMPONENT_TRUSTED_ROBOT];
359 3
        $b = $info[$this->kernel::REASON_COMPONENT_TRUSTED_ROBOT];
360 3
        $data['component_trustedbot'] = $a;
361 3
        $data['rule_list']['trustedbot'] = $b;
362

363 3
        return $data;
364
    }
365

366
    /**
367
     * Get counter default.
368
     *
369
     * @return array
370
     */
371 3
    private function getCounterDefault(): array
372
    {
373 3
        $counter = [];
374

375 3
        $counter[$this->kernel::REASON_DENY_IP]                 = 0;
376 3
        $counter[$this->kernel::REASON_COMPONENT_IP]            = 0;
377 3
        $counter[$this->kernel::REASON_COMPONENT_RDNS]          = 0;
378 3
        $counter[$this->kernel::REASON_COMPONENT_HEADER]        = 0;
379 3
        $counter[$this->kernel::REASON_COMPONENT_USERAGENT]     = 0;
380 3
        $counter[$this->kernel::REASON_COMPONENT_TRUSTED_ROBOT] = 0;
381 3
        $counter[$this->kernel::REASON_TOO_MANY_ACCESSES]       = 0;
382 3
        $counter[$this->kernel::REASON_REACHED_LIMIT_DAY]       = 0;
383 3
        $counter[$this->kernel::REASON_REACHED_LIMIT_HOUR]      = 0;
384 3
        $counter[$this->kernel::REASON_REACHED_LIMIT_MINUTE]    = 0;
385 3
        $counter[$this->kernel::REASON_REACHED_LIMIT_SECOND]    = 0;
386 3
        $counter[$this->kernel::REASON_EMPTY_REFERER]           = 0;
387 3
        $counter[$this->kernel::REASON_EMPTY_JS_COOKIE]         = 0;
388 3
        $counter[$this->kernel::REASON_TOO_MANY_SESSIONS]       = 0;
389

390 3
        return $counter;
391
    }
392

393
    /**
394
     * Get info default.
395
     *
396
     * @return array
397
     */
398 3
    private function getInfoDefault(): array
399
    {
400 3
        $info = [];
401

402 3
        $info[$this->kernel::REASON_DENY_IP]                 = [];
403 3
        $info[$this->kernel::REASON_COMPONENT_IP]            = [];
404 3
        $info[$this->kernel::REASON_COMPONENT_RDNS]          = [];
405 3
        $info[$this->kernel::REASON_COMPONENT_HEADER]        = [];
406 3
        $info[$this->kernel::REASON_COMPONENT_USERAGENT]     = [];
407 3
        $info[$this->kernel::REASON_COMPONENT_TRUSTED_ROBOT] = [];
408 3
        $info[$this->kernel::REASON_DENY_IP]                 = [];
409 3
        $info[$this->kernel::REASON_REACHED_LIMIT_DAY]       = [];
410 3
        $info[$this->kernel::REASON_REACHED_LIMIT_HOUR]      = [];
411 3
        $info[$this->kernel::REASON_REACHED_LIMIT_MINUTE]    = [];
412 3
        $info[$this->kernel::REASON_REACHED_LIMIT_SECOND]    = [];
413 3
        $info[$this->kernel::REASON_EMPTY_REFERER]           = [];
414 3
        $info[$this->kernel::REASON_EMPTY_JS_COOKIE]         = [];
415 3
        $info[$this->kernel::REASON_TOO_MANY_SESSIONS]       = [];
416

417 3
        return $info;
418
    }
419
}

Read our documentation on viewing source code .

Loading