Passed
Pull Request — master (#795)
by
unknown
04:36
created

TAuthorizationRule::_getZappableSleepProps()   F

Complexity

Conditions 12
Paths 512

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
cc 12
eloc 19
c 0
b 0
f 0
nc 512
nop 1
dl 0
loc 30
rs 3.4777
ccs 0
cts 0
cp 0
crap 156

How to fix   Complexity   

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
 * TAuthorizationRule, TAuthorizationRuleCollection class file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 * @package Prado\Security
9
 */
10
11
namespace Prado\Security;
12
13
use Prado\Exceptions\TInvalidDataValueException;
14
15
/**
16
 * TAuthorizationRule class
17
 *
18
 * TAuthorizationRule represents a single authorization rule.
19
 * A rule is specified by an action (required), a list of users (optional),
20
 * a list of roles (optional), a verb (optional), and a list of IP rules (optional).
21
 * Action can be either 'allow' or 'deny'.
22
 * Guest (anonymous, unauthenticated) users are represented by question mark '?'.
23
 * All users (including guest users) are represented by asterisk '*'.
24
 * Authenticated users are represented by '@'.
25
 * Users/roles are case-insensitive.
26
 * Different users/roles are separated by comma ','.
27
 * Verb can be either 'get' or 'post'. If it is absent, it means both.
28
 * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*')
29
 *
30
 * @author Qiang Xue <[email protected]>
31
 * @package Prado\Security
32
 * @since 3.0
33
 */
34
class TAuthorizationRule extends \Prado\TComponent implements \Prado\Collections\IPriorityItem
35
{
36
	/**
37
	 * @var string action, either 'allow' or 'deny'
38
	 */
39
	private $_action = 'allow';
40
	/**
41
	 * @var array list of user IDs
42
	 */
43
	private $_users = [];
44
	/**
45
	 * @var array list of roles
46
	 */
47
	private $_roles = ['*'];
48
	/**
49
	 * @var string verb, may be empty, 'get', or 'post'.
50
	 */
51
	private $_verb = '*';
52
	/**
53
	 * @var string IP patterns
54
	 */
55
	private $_ipRules = ['*'];
56
	/**
57
	 * @var numeric priority of the rule
0 ignored issues
show
Bug introduced by
The type Prado\Security\numeric 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...
58
	 */
59
	private $_priority;
60
	/**
61
	 * @var bool if this rule applies to everyone
62
	 */
63
	private $_everyone = true;
64
	/**
65
	 * @var bool if this rule applies to guest user
66
	 */
67
	private $_guest = false;
68
	/**
69
	 * @var bool if this rule applies to authenticated users
70
	 */
71
	private $_authenticated = false;
72
73
	/**
74
	 * Constructor.
75
	 * @param string $action action, either 'deny' or 'allow'
76
	 * @param string $users a comma separated user list
77
	 * @param string $roles a comma separated role list
78
	 * @param string $verb verb, can be empty, 'get', or 'post'
79
	 * @param string $ipRules IP rules (separated by comma, can contain wild card *)
80
	 * @param null|numeric $priority
81
	 */
82
	public function __construct($action = 'allow', $users = '', $roles = '', $verb = '', $ipRules = '', $priority = '')
83
	{
84
		$action = strtolower(trim($action));
85
		if ($action === 'allow' || $action === 'deny') {
86
			$this->_action = $action;
87
		} else {
88
			throw new TInvalidDataValueException('authorizationrule_action_invalid', $action);
89
		}
90
		
91
		$this->_users = [];
92
		$this->_everyone = false;
93
		$this->_guest = false;
94
		$this->_authenticated = false;
95
		if (trim($users) === '') {
96
			$users = '*';
97
		}
98
		foreach (explode(',', $users) as $user) {
99
			if (($user = trim(strtolower($user))) !== '') {
100
				if ($user === '*') {
101
					$this->_everyone = true;
102
					break;
103
				} elseif ($user === '?') {
104
					$this->_guest = true;
105
				} elseif ($user === '@') {
106
					$this->_authenticated = true;
107
				} else {
108
					$this->_users[] = $user;
109
				}
110
			}
111
		}
112
		
113
		$this->_roles = [];
114
		if (trim($roles) === '') {
115
			$roles = '*';
116
		}
117
		foreach (explode(',', $roles) as $role) {
118
			if (($role = trim(strtolower($role))) !== '') {
119
				$this->_roles[] = $role;
120
			}
121
		}
122
		
123
		if (($verb = trim(strtolower($verb))) === '') {
124
			$verb = '*';
125
		}
126
		if ($verb === '*' || $verb === 'get' || $verb === 'post') {
127
			$this->_verb = $verb;
128
		} else {
129
			throw new TInvalidDataValueException('authorizationrule_verb_invalid', $verb);
130
		}
131
		
132
		$this->_ipRules = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $_ipRules.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
133
		if (trim($ipRules) === '') {
134
			$ipRules = '*';
135
		}
136
		foreach (explode(',', $ipRules) as $ipRule) {
137
			if (($ipRule = trim($ipRule)) !== '') {
138
				$this->_ipRules[] = $ipRule;
139
			}
140
		}
141
		
142
		$this->_priority = is_numeric($priority) ? $priority : null;
0 ignored issues
show
Documentation Bug introduced by
It seems like is_numeric($priority) ? $priority : null can also be of type string. However, the property $_priority is declared as type Prado\Security\numeric. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
143
144
		parent::__construct();
145
	}
146
147
	/**
148
	 * @return string action, either 'allow' or 'deny'
149
	 */
150
	public function getAction()
151
	{
152
		return $this->_action;
153
	}
154
155
	/**
156
	 * @return string[] list of user IDs
157
	 */
158
	public function getUsers()
159
	{
160
		return $this->_users;
161
	}
162
163
	/**
164
	 * @return string[] list of roles
165
	 */
166
	public function getRoles()
167
	{
168
		return $this->_roles;
169
	}
170
171
	/**
172
	 * @return string verb, may be '*', 'get', or 'post'.
173
	 */
174
	public function getVerb()
175
	{
176
		return $this->_verb;
177
	}
178
179
	/**
180
	 * @return array list of IP rules.
181
	 * @since 3.1.1
182
	 */
183
	public function getIPRules()
184
	{
185
		return $this->_ipRules;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_ipRules returns the type string which is incompatible with the documented return type array.
Loading history...
186
	}
187
188
	/**
189
	 * @return numeric priority of the rule.
190
	 * @since 4.2.0
191
	 */
192
	public function getPriority()
193
	{
194
		return $this->_priority;
195
	}
196
197
	/**
198
	 * @return bool if this rule applies to everyone
199
	 */
200
	public function getGuestApplied()
201
	{
202
		return $this->_guest || $this->_everyone;
203
	}
204
205
	/**
206
	 * @return bool if this rule applies to everyone
207
	 */
208
	public function getEveryoneApplied()
209
	{
210
		return $this->_everyone;
211
	}
212
213
	/**
214
	 * @return bool if this rule applies to authenticated users
215
	 */
216
	public function getAuthenticatedApplied()
217
	{
218
		return $this->_authenticated || $this->_everyone;
219
	}
220
221
	/**
222
	 * @param IUser $user the user object
223
	 * @param string $verb the request verb (GET, PUT)
224
	 * @param string $ip the request IP address
225
	 * @param null|mixed $extra
226
	 * @return int 1 if the user is allowed, -1 if the user is denied, 0 if the rule does not apply to the user
227
	 */
228
	public function isUserAllowed(IUser $user, $verb, $ip, $extra = null)
0 ignored issues
show
Unused Code introduced by
The parameter $extra is not used and could be removed. ( Ignorable by Annotation )

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

228
	public function isUserAllowed(IUser $user, $verb, $ip, /** @scrutinizer ignore-unused */ $extra = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
229
	{
230
		if ($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user)) {
231
			return ($this->_action === 'allow') ? 1 : -1;
232
		} else {
233
			return 0;
234
		}
235
	}
236
237
	private function isIpMatched($ip)
238
	{
239
		if (empty($this->_ipRules)) {
240
			return 1;
241
		}
242
		foreach ($this->_ipRules as $rule) {
0 ignored issues
show
Bug introduced by
The expression $this->_ipRules of type string is not traversable.
Loading history...
243
			if ($rule === '*' || $rule === $ip || (($pos = strpos($rule, '*')) !== false && strncmp($ip, $rule, $pos) === 0)) {
244
				return 1;
245
			}
246
		}
247
		return 0;
248
	}
249
250
	private function isUserMatched($user)
251
	{
252
		return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()), $this->_users));
253
	}
254
255
	private function isRoleMatched($user)
256
	{
257
		foreach ($this->_roles as $role) {
258
			if ($role === '*' || $user->isInRole($role)) {
259
				return true;
260
			}
261
		}
262
		return false;
263
	}
264
265
	private function isVerbMatched($verb)
266
	{
267
		return ($this->_verb === '*' || strcasecmp($verb, $this->_verb) === 0);
268
	}
269
	
270
	/**
271
	 * Returns an array with the names of all variables of this object that should NOT be serialized
272
	 * because their value is the default one or useless to be cached for the next load.
273
	 * Reimplement in derived classes to add new variables, but remember to also to call the parent
274
	 * implementation first.
275
	 * @param array $exprops by reference
276
	 */
277
	protected function _getZappableSleepProps(&$exprops)
278
	{
279
		parent::_getZappableSleepProps($exprops);
280
		
281
		if ($this->_action === 'allow') {
282
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_action";
283
		}
284
		if ($this->_users === []) {
285
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_users";
286
		}
287
		if (count($this->_roles) === 1 && $this->_roles[0] === '*') {
288
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_roles";
289
		}
290
		if ($this->_verb === '*') {
291
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_verb";
292
		}
293
		if (count($this->_ipRules) === 1 && $this->_ipRules[0] === '*') {
0 ignored issues
show
Bug introduced by
$this->_ipRules of type string is incompatible with the type Countable|array expected by parameter $value of count(). ( Ignorable by Annotation )

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

293
		if (count(/** @scrutinizer ignore-type */ $this->_ipRules) === 1 && $this->_ipRules[0] === '*') {
Loading history...
294
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_ipRules";
295
		}
296
		if ($this->_everyone == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
297
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_everyone";
298
		}
299
		if ($this->_guest == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
300
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_guest";
301
		}
302
		if ($this->_authenticated == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
303
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_authenticated";
304
		}
305
		if ($this->_priority === null) {
306
			$exprops[] = "\0Prado\Security\TAuthorizationRule\0_priority";
307
		}
308
	}
309
}
310