AccessFilter   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 236
Duplicated Lines 19.49 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 46
loc 236
c 0
b 0
f 0
wmc 46
lcom 1
cbo 5
rs 8.3999

8 Methods

Rating   Name   Duplication   Size   Complexity  
C pre() 8 25 7
B checkRule() 0 14 6
A matchAction() 0 12 3
D matchUser() 0 37 10
B matchRole() 18 19 5
C matchIP() 0 22 9
B matchVerb() 20 21 5
A post() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AccessFilter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AccessFilter, and based on these observations, apply Extract Interface, too.

1
<?php /** AccessFilterMicro */
2
3
namespace Micro\Filter;
4
5
use Micro\Base\Exception;
6
use Micro\Web\IUser;
7
use Micro\Web\RequestInjector;
8
use Micro\Web\UserInjector;
9
use Psr\Http\Message\ServerRequestInterface;
10
11
/**
12
 * Class AccessFilter
13
 *
14
 * @author Oleg Lunegov <[email protected]>
15
 * @link https://github.com/linpax/microphp-framework
16
 * @copyright Copyright (c) 2013 Oleg Lunegov
17
 * @license https://github.com/linpax/microphp-framework/blob/master/LICENSE
18
 * @package Micro
19
 * @subpackage Filter
20
 * @version 1.0
21
 * @since 1.0
22
 */
23
class AccessFilter extends Filter
24
{
25
    /** @var IUser $user */
26
    protected $user;
27
    /** @var ServerRequestInterface $request */
28
    protected $request;
29
30
    /**
31
     * @param array $params
32
     * @return bool
33
     * @throws Exception
34
     */
35
    public function pre(array $params)
36
    {
37
        $this->user = (new UserInjector)->build();
38
        $this->request = (new RequestInjector)->build();
39
40
        /** @noinspection ForeachSourceInspection */
41
        foreach ($params['rules'] AS $rule) {
42
            $res = $this->checkRule($rule);
43
44
            if ($res === true) {
45
                return true;
46 View Code Duplication
            } elseif ($res === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
47
                $this->result = [
48
                    'redirect' => !empty($rule['redirect']) ? $rule['redirect'] : null,
49
                    'message' => !empty($rule['message']) ? $rule['message'] : 'Access denied!'
50
                ];
51
52
                return false;
53
            } elseif ($res === null) {
54
                continue;
55
            }
56
        }
57
58
        return true;
59
    }
60
61
    /**
62
     * Check one rule
63
     *
64
     * @access protected
65
     *
66
     * @param array $rule rule definition
67
     *
68
     * @return bool|null
69
     * @throws Exception
70
     */
71
    protected function checkRule(array $rule)
72
    {
73
        if (
74
            $this->matchAction($rule)
75
            && $this->matchUser($rule)
76
            && $this->matchRole($rule)
77
            && $this->matchIP($rule)
78
            && $this->matchVerb($rule)
79
        ) {
80
            return $rule['allow'];
81
        } else {
82
            return null;
83
        }
84
    }
85
86
    /**
87
     * Match action
88
     *
89
     * @access protected
90
     *
91
     * @param array $rule rule definition
92
     *
93
     * @return bool
94
     */
95
    protected function matchAction($rule)
96
    {
97
        if (empty($rule['actions'])) {
98
            return true;
99
        }
100
101
        if (is_array($rule['actions'])) {
102
            return in_array($this->action, $rule['actions'], true);
103
        }
104
105
        return $this->action === $rule['actions'];
106
    }
107
108
    /**
109
     * Match user
110
     *
111
     * @access protected
112
     *
113
     * @param array $rule rule definition
114
     *
115
     * @return bool
116
     * @throws Exception
117
     */
118
    protected function matchUser($rule)
119
    {
120
        if (empty($rule['users'])) {
121
            return true;
122
        }
123
124
        if (!is_array($rule['users'])) {
125
            $rule['users'][] = $rule['users'];
126
        }
127
128
        /** @noinspection ForeachSourceInspection */
129
        foreach ($rule['users'] AS $u) {
130
            switch ($u) {
131
                case '*':
132
                    return true;
133
134
                case '?':
135
                    if ($this->user->isGuest()) {
136
                        return true;
137
                    }
138
                    break;
139
140
                case '@':
141
                    if (!$this->user->isGuest()) {
142
                        return true;
143
                    }
144
                    break;
145
146
                default:
147
                    if ($this->user->getID() === $u) {
148
                        return true;
149
                    }
150
            }
151
        }
152
153
        return false;
154
    }
155
156
    /**
157
     * Match role
158
     *
159
     * @access protected
160
     *
161
     * @param array $rule rule definition
162
     *
163
     * @return bool
164
     * @throws Exception
165
     */
166 View Code Duplication
    protected function matchRole($rule)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168
        if (empty($rule['roles'])) {
169
            return true;
170
        }
171
172
        if (!is_array($rule['roles'])) {
173
            $rule['roles'][] = $rule['roles'];
174
        }
175
176
        /** @noinspection ForeachSourceInspection */
177
        foreach ($rule['roles'] AS $role) {
178
            if ($this->user->check($role)) {
179
                return true;
180
            }
181
        }
182
183
        return false;
184
    }
185
186
    /**
187
     * Match IP
188
     *
189
     * @access protected
190
     *
191
     * @param array $rule rule definition
192
     *
193
     * @return bool
194
     * @throws Exception
195
     */
196
    protected function matchIP($rule)
197
    {
198
        if (empty($rule['ips'])) {
199
            return true;
200
        }
201
202
        if (!is_array($rule['ips'])) {
203
            $rule['ips'][] = $rule['ips'];
204
        }
205
206
        $server = $this->request->getServerParams();
207
        $userIp = ($ip = filter_var($server['REMOTE_ADDR'], FILTER_VALIDATE_IP)) ? $ip : '127.0.0.1';
208
209
        /** @noinspection ForeachSourceInspection */
210
        foreach ($rule['ips'] AS $r) {
211
            if ($r === '*' || $r === $userIp || (($pos = strpos($r, '*')) !== false && 0 === strpos($userIp, $r))) {
212
                return true;
213
            }
214
        }
215
216
        return false;
217
    }
218
219
    /**
220
     * Match verbose
221
     *
222
     * @access protected
223
     *
224
     * @param array $rule rule definition
225
     *
226
     * @return bool
227
     * @throws Exception
228
     */
229 View Code Duplication
    protected function matchVerb($rule)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
    {
231
        if (empty($rule['verb'])) {
232
            return true;
233
        }
234
235
        if (!is_array($rule['verb'])) {
236
            $rule['verb'][] = $rule['verb'];
237
        }
238
239
        $verb = $this->request->getMethod();
240
241
        /** @noinspection ForeachSourceInspection */
242
        foreach ($rule['verb'] AS $v) {
243
            if ($v === $verb) {
244
                return true;
245
            }
246
        }
247
248
        return false;
249
    }
250
251
    /**
252
     * @inheritdoc
253
     */
254
    public function post(array $params)
255
    {
256
        return $params['data'];
257
    }
258
}
259