1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use yii\base\Component;
use yii\base\Action;
/**
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class AccessRule extends Component
{
/**
* @var boolean whether this is an 'allow' rule or 'deny' rule.
*/
public $allow;
/**
* @var array list of action IDs that this rule applies to. The comparison is case-sensitive.
* If not set or empty, it means this rule applies to all actions.
*/
public $actions;
/**
* @var array list of controller IDs that this rule applies to. The comparison is case-sensitive.
* If not set or empty, it means this rule applies to all controllers.
*/
public $controllers;
/**
* @var array list of roles that this rule applies to. Two special roles are recognized, and
* they are checked via [[User::isGuest]]:
*
* - `?`: matches a guest user (not authenticated yet)
* - `@`: matches an authenticated user
*
* Using additional role names requires RBAC (Role-Based Access Control), and
* [[User::hasAccess()]] will be called.
*
* If this property is not set or empty, it means this rule applies to all roles.
*/
public $roles;
/**
* @var array list of user IP addresses that this rule applies to. An IP address
* can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix.
* For example, '192.168.*' matches all IP addresses in the segment '192.168.'.
* If not set or empty, it means this rule applies to all IP addresses.
* @see Request::userIP
*/
public $ips;
/**
* @var array list of request methods (e.g. `GET`, `POST`) that this rule applies to.
* The request methods must be specified in uppercase.
* If not set or empty, it means this rule applies to all request methods.
* @see Request::requestMethod
*/
public $verbs;
/**
* @var callback a callback that will be called to determine if the rule should be applied.
* The signature of the callback should be as follows:
*
* ~~~
* function ($rule, $action)
* ~~~
*
* where `$rule` is this rule, and `$action` is the current [[Action|action]] object.
* The callback should return a boolean value indicating whether this rule should be applied.
*/
public $matchCallback;
/**
* @var callback a callback that will be called if this rule determines the access to
* the current action should be denied. If not set, the behavior will be determined by
* [[AccessControl]].
*
* The signature of the callback should be as follows:
*
* ~~~
* function ($rule, $action)
* ~~~
*
* where `$rule` is this rule, and `$action` is the current [[Action|action]] object.
*/
public $denyCallback;
/**
* Checks whether the Web user is allowed to perform the specified action.
* @param Action $action the action to be performed
* @param User $user the user object
* @param Request $request
* @return boolean|null true if the user is allowed, false if the user is denied, null if the rule does not apply to the user
*/
public function allows($action, $user, $request)
{
if ($this->matchAction($action)
&& $this->matchRole($user)
&& $this->matchIP($request->getUserIP())
&& $this->matchVerb($request->getRequestMethod())
&& $this->matchController($action->controller)
&& $this->matchCustom($action)
) {
return $this->allow ? true : false;
} else {
return null;
}
}
/**
* @param Action $action the action
* @return boolean whether the rule applies to the action
*/
protected function matchAction($action)
{
return empty($this->actions) || in_array($action->id, $this->actions, true);
}
/**
* @param Controller $controller the controller
* @return boolean whether the rule applies to the controller
*/
protected function matchController($controller)
{
return empty($this->controllers) || in_array($controller->uniqueId, $this->controllers, true);
}
/**
* @param User $user the user object
* @return boolean whether the rule applies to the role
*/
protected function matchRole($user)
{
if (empty($this->roles)) {
return true;
}
foreach ($this->roles as $role) {
if ($role === '?' && $user->getIsGuest()) {
return true;
} elseif ($role === '@' && !$user->getIsGuest()) {
return true;
} elseif ($user->checkAccess($role)) {
return true;
}
}
return false;
}
/**
* @param string $ip the IP address
* @return boolean whether the rule applies to the IP address
*/
protected function matchIP($ip)
{
if (empty($this->ips)) {
return true;
}
foreach ($this->ips as $rule) {
if ($rule === '*' || $rule === $ip || (($pos = strpos($rule, '*')) !== false && !strncmp($ip, $rule, $pos))) {
return true;
}
}
return false;
}
/**
* @param string $verb the request method
* @return boolean whether the rule applies to the request
*/
protected function matchVerb($verb)
{
return empty($this->verbs) || in_array($verb, $this->verbs, true);
}
/**
* @param Action $action the action to be performed
* @return boolean whether the rule should be applied
*/
protected function matchCustom($action)
{
return empty($this->matchCallback) || call_user_func($this->matchCallback, $this, $action);
}
}