Passed
Push — 2.x ( a336cc...b20143 )
by Terry
01:59
created

RuleTrait::DoesRuleExist()   B

Complexity

Conditions 9
Paths 66

Size

Total Lines 71
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 38
nc 66
nop 0
dl 0
loc 71
rs 7.7564
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Traits\Kernel;
14
15
use Shieldon\Firewall\Kernel;
16
use function file_exists;
17
use function file_put_contents;
18
use function filter_var;
19
use function is_writable;
20
use function time;
21
22
/*
23
 * @since 1.0.0
24
 */
25
trait RuleTrait
26
{
27
    /**
28
     * Look up the rule table.
29
     *
30
     * If a specific IP address doesn't exist, return false. 
31
     * Otherwise, return true.
32
     *
33
     * @return bool
34
     */
35
    private function DoesRuleExist()
36
    {
37
        $ipRule = $this->driver->get($this->ip, 'rule');
38
39
        if (empty($ipRule)) {
40
            return false;
41
        }
42
43
        $ruleType = (int) $ipRule['type'];
44
45
        // Apply the status code.
46
        $this->result = $ruleType;
0 ignored issues
show
Bug Best Practice introduced by
The property result does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
47
48
        if ($ruleType === kernel::ACTION_ALLOW) {
49
            return true;
50
        }
51
52
        // Current visitor has been blocked. If he still attempts accessing the site, 
53
        // then we can drop him into the permanent block list.
54
        $attempts = $ipRule['attempts'] ?? 0;
55
        $attempts = (int) $attempts;
56
        $now = time();
57
        $logData = [];
58
        $handleType = 0;
59
60
        $logData['log_ip']     = $ipRule['log_ip'];
61
        $logData['ip_resolve'] = $ipRule['ip_resolve'];
62
        $logData['time']       = $now;
63
        $logData['type']       = $ipRule['type'];
64
        $logData['reason']     = $ipRule['reason'];
65
        $logData['attempts']   = $attempts;
66
67
        // @since 0.2.0
68
        $attemptPeriod = $this->properties['record_attempt_detection_period'];
69
        $attemptReset  = $this->properties['reset_attempt_counter'];
70
71
        $lastTimeDiff = $now - $ipRule['time'];
72
73
        if ($lastTimeDiff <= $attemptPeriod) {
74
            $logData['attempts'] = ++$attempts;
75
        }
76
77
        if ($lastTimeDiff > $attemptReset) {
78
            $logData['attempts'] = 0;
79
        }
80
81
        if ($ruleType === kernel::ACTION_TEMPORARILY_DENY) {
82
            $ratd = $this->determineAttemptsTemporaryDeny($logData, $handleType, $attempts);
83
            $logData = $ratd['log_data'];
84
            $handleType = $ratd['handle_type'];
85
        }
86
87
        if ($ruleType === kernel::ACTION_DENY) {
88
            $rapd = $this->determineAttemptsPermanentDeny($logData, $handleType, $attempts);
89
            $logData = $rapd['log_data'];
90
            $handleType = $rapd['handle_type'];
91
        }
92
93
        // We only update data when `deny_attempt_enable` is enable.
94
        // Because we want to get the last visited time and attempt counter.
95
        // Otherwise, we don't update it everytime to avoid wasting CPU resource.
96
        if ($this->event['update_rule_table']) {
97
            $this->driver->save($this->ip, $logData, 'rule');
98
        }
99
100
        // Notify this event to messenger.
101
        if ($this->event['trigger_messengers']) {
102
            $this->prepareMessengerBody($logData, $handleType);
0 ignored issues
show
Bug introduced by
It seems like prepareMessengerBody() 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

102
            $this->/** @scrutinizer ignore-call */ 
103
                   prepareMessengerBody($logData, $handleType);
Loading history...
103
        }
104
105
        return true;
106
    }
107
108
    /**
109
     * Record the attempts when the user is temporarily denied by rule table.
110
     *
111
     * @param array $logData
112
     * @param int   $handleType
113
     * @param int   $attempts
114
     * 
115
     * @return array
116
     */
117
    private function determineAttemptsTemporaryDeny(array $logData, int $handleType, int $attempts): array
118
    {
119
        if ($this->properties['deny_attempt_enable']['data_circle']) {
120
            $this->event['update_rule_table'] = true;
0 ignored issues
show
Bug Best Practice introduced by
The property event does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
121
122
            $buffer = $this->properties['deny_attempt_buffer']['data_circle'];
123
124
            if ($attempts >= $buffer) {
125
126
                if ($this->properties['deny_attempt_notify']['data_circle']) {
127
                    $this->event['trigger_messengers'] = true;
128
                }
129
130
                $logData['type'] = kernel::ACTION_DENY;
131
132
                // Reset this value for next checking process - iptables.
133
                $logData['attempts'] = 0;
134
                $handleType = 1;
135
            }
136
        }
137
138
        return [
139
            'log_data' => $logData,
140
            'handle_type' => $handleType,
141
        ];
142
    }
143
144
    /**
145
     * Record the attempts when the user is permanently denied by rule table.
146
     *
147
     * @param array $logData
148
     * @param int   $handleType
149
     * @param int   $attempts
150
     * 
151
     * @return array
152
     */
153
    private function determineAttemptsPermanentDeny(array $logData, int $handleType, int $attempts): array
154
    {
155
        if ($this->properties['deny_attempt_enable']['system_firewall']) {
156
            $this->event['update_rule_table'] = true;
0 ignored issues
show
Bug Best Practice introduced by
The property event does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
157
158
            // For the requests that are already banned, but they are still attempting access, that means 
159
            // that they are programmably accessing your website. Consider put them in the system-layer fireall
160
            // such as IPTABLE.
161
            $bufferIptable = $this->properties['deny_attempt_buffer']['system_firewall'];
162
163
            if ($attempts >= $bufferIptable) {
164
165
                if ($this->properties['deny_attempt_notify']['system_firewall']) {
166
                    $this->event['trigger_messengers'] = true;
167
                }
168
169
                $folder = rtrim($this->properties['iptables_watching_folder'], '/');
170
171
                if (file_exists($folder) && is_writable($folder)) {
172
                    $filePath = $folder . '/iptables_queue.log';
173
174
                    // command, ipv4/6, ip, subnet, port, protocol, action
175
                    // add,4,127.0.0.1,null,all,all,drop  (example)
176
                    // add,4,127.0.0.1,null,80,tcp,drop   (example)
177
                    $command = 'add,4,' . $this->ip . ',null,all,all,deny';
178
179
                    if (filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
180
                        $command = 'add,6,' . $this->ip . ',null,all,allow';
181
                    }
182
183
                    // Add this IP address to itables_queue.log
184
                    // Use `bin/iptables.sh` for adding it into IPTABLES. See document for more information. 
185
                    file_put_contents($filePath, $command . "\n", FILE_APPEND | LOCK_EX);
186
187
                    $logData['attempts'] = 0;
188
                    $handleType = 2;
189
                }
190
            }
191
        }
192
193
        return [
194
            'log_data' => $logData,
195
            'handle_type' => $handleType,
196
        ];
197
    }
198
}
199