1
|
|
|
<?php /** MicroRBAC */ |
2
|
|
|
|
3
|
|
|
namespace Micro\Auth\Drivers; |
4
|
|
|
|
5
|
|
|
use Micro\Auth\Adapter; |
6
|
|
|
use Micro\Db\Drivers\IDriver; |
7
|
|
|
use Micro\Db\IConnection; |
8
|
|
|
use Micro\Mvc\Models\Query; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Abstract RBAC class file. |
12
|
|
|
* |
13
|
|
|
* @author Oleg Lunegov <[email protected]> |
14
|
|
|
* @link https://github.com/linpax/microphp-framework |
15
|
|
|
* @copyright Copyright (c) 2013 Oleg Lunegov |
16
|
|
|
* @license https://github.com/linpax/microphp-framework/blob/master/LICENSE |
17
|
|
|
* @package Micro |
18
|
|
|
* @subpackage Auth\Drivers |
19
|
|
|
* @version 1.0 |
20
|
|
|
* @since 1.0 |
21
|
|
|
*/ |
22
|
|
|
abstract class Rbac implements Adapter |
23
|
|
|
{ |
24
|
|
|
/** @const integer TYPE_ROLE */ |
25
|
|
|
const TYPE_ROLE = 0; |
26
|
|
|
/** @const integer TYPE_PERMISSION */ |
27
|
|
|
const TYPE_PERMISSION = 1; |
28
|
|
|
/** @const integer TYPE_OPERATION */ |
29
|
|
|
const TYPE_OPERATION = 2; |
30
|
|
|
|
31
|
|
|
/** @var IDriver $db */ |
32
|
|
|
protected $db; |
33
|
|
|
|
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Based constructor for RBAC rules |
37
|
|
|
* |
38
|
|
|
* @access public |
39
|
|
|
* |
40
|
|
|
* @param IConnection $connection |
41
|
|
|
* |
42
|
|
|
* @result void |
43
|
|
|
*/ |
44
|
|
|
public function __construct(IConnection $connection) |
45
|
|
|
{ |
46
|
|
|
$this->db = $connection->getDriver(); |
47
|
|
|
|
48
|
|
|
if (!$this->db->tableExists('rbac_user')) { |
49
|
|
|
$this->db->createTable('rbac_user', [ |
50
|
|
|
'`role` varchar(127) NOT NULL', |
51
|
|
|
'`user` int(10) unsigned NOT NULL', |
52
|
|
|
'UNIQUE KEY `name` (`name`,`user`)' |
53
|
|
|
], 'ENGINE=MyISAM DEFAULT CHARSET=utf8'); |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Assign RBAC element into user |
59
|
|
|
* |
60
|
|
|
* @access public |
61
|
|
|
* |
62
|
|
|
* @param integer $userId user id |
63
|
|
|
* @param string $name element name |
64
|
|
|
* |
65
|
|
|
* @return bool |
66
|
|
|
*/ |
67
|
|
|
abstract public function assign($userId, $name); |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Check privileges to operation |
71
|
|
|
* |
72
|
|
|
* @access public |
73
|
|
|
* |
74
|
|
|
* @param integer $userId user id |
75
|
|
|
* @param string $permission permission name |
76
|
|
|
* @param array $data action params |
77
|
|
|
* |
78
|
|
|
* @return boolean |
79
|
|
|
* @throws \Micro\Base\Exception |
80
|
|
|
*/ |
81
|
|
|
public function check($userId, $permission, array $data = []) |
82
|
|
|
{ |
83
|
|
|
$rawRoles = $this->rawRoles(); |
84
|
|
|
$tree = $this->tree($rawRoles); |
85
|
|
|
|
86
|
|
|
/** @var array $roles */ |
87
|
|
|
$roles = $this->assigned($userId); |
88
|
|
|
if (!$roles) { |
|
|
|
|
89
|
|
|
return false; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
foreach ($roles AS $role) { |
93
|
|
|
|
94
|
|
|
$actionRole = $this->searchRoleRecursive($tree, $role['name']); |
95
|
|
|
if ($actionRole) { |
96
|
|
|
/** @var array $trustRole */ |
97
|
|
|
$trustRole = $this->searchRoleRecursive($actionRole, $permission); |
98
|
|
|
if ($trustRole) { |
|
|
|
|
99
|
|
|
return $this->execute($trustRole[$permission], $data); |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return false; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Get raw roles |
109
|
|
|
* |
110
|
|
|
* @access public |
111
|
|
|
* @return mixed |
112
|
|
|
*/ |
113
|
|
|
abstract public function rawRoles(); |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Build tree from RBAC rules |
117
|
|
|
* |
118
|
|
|
* @access public |
119
|
|
|
* |
120
|
|
|
* @param array $elements elements array |
121
|
|
|
* @param string $parentId parent ID |
122
|
|
|
* |
123
|
|
|
* @return array |
124
|
|
|
*/ |
125
|
|
|
public function tree(&$elements, $parentId = '0') |
126
|
|
|
{ |
127
|
|
|
$branch = []; |
128
|
|
|
foreach ($elements AS $key => $element) { |
129
|
|
|
if ($element['based'] === (string)$parentId) { |
130
|
|
|
$children = $this->tree($elements, $element['name']); |
131
|
|
|
|
132
|
|
|
if ($children) { |
133
|
|
|
$element['childs'] = $children; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
$branch[$element['name']] = $element; |
137
|
|
|
unset($elements[$key]); |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return $branch; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Get assigned to user RBAC elements |
146
|
|
|
* |
147
|
|
|
* @access public |
148
|
|
|
* |
149
|
|
|
* @param integer $userId user ID |
150
|
|
|
* |
151
|
|
|
* @return mixed |
152
|
|
|
* @throws \Micro\Base\Exception |
153
|
|
|
*/ |
154
|
|
|
public function assigned($userId) |
155
|
|
|
{ |
156
|
|
|
$query = new Query($this->db); |
157
|
|
|
$query->distinct = true; |
158
|
|
|
$query->select = $this->db->getDriverType() === 'pgsql' ? '"role" AS "name"' : '`role` AS `name`'; |
159
|
|
|
$query->table = $this->db->getDriverType() === 'pgsql' ? '"rbac_user"' : '`rbac_user`'; |
160
|
|
|
$query->addWhere(($this->db->getDriverType() === 'pgsql' ? '"user"=' : '`user`=').$userId); |
161
|
|
|
$query->single = false; |
162
|
|
|
|
163
|
|
|
return $query->run(\PDO::FETCH_ASSOC); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Recursive search in roles array |
168
|
|
|
* |
169
|
|
|
* @access public |
170
|
|
|
* |
171
|
|
|
* @param array $roles elements |
172
|
|
|
* @param string $finder element name to search |
173
|
|
|
* |
174
|
|
|
* @return array|false |
175
|
|
|
*/ |
176
|
|
|
protected function searchRoleRecursive($roles, $finder) |
177
|
|
|
{ |
178
|
|
|
$result = false; |
179
|
|
|
foreach ($roles AS $id => $role) { |
180
|
|
|
if ($id === $finder) { |
181
|
|
|
$result = [$id => $role]; |
182
|
|
|
break; |
183
|
|
|
} else { |
184
|
|
|
if (!empty($role['childs'])) { |
185
|
|
|
$result = $this->searchRoleRecursive($role['childs'], $finder); |
186
|
|
|
break; |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
return $result; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Execute rule |
196
|
|
|
* |
197
|
|
|
* @access public |
198
|
|
|
* |
199
|
|
|
* @param array $role element |
200
|
|
|
* @param array $data action params |
201
|
|
|
* |
202
|
|
|
* @return bool |
203
|
|
|
*/ |
204
|
|
|
public function execute(array $role, array $data) |
205
|
|
|
{ |
206
|
|
|
if (!$role['data']) { |
207
|
|
|
return true; |
208
|
|
|
} else { |
209
|
|
|
extract($data, EXTR_OVERWRITE); |
210
|
|
|
|
211
|
|
|
return eval('return '.$role['data']); |
|
|
|
|
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Revoke RBAC element from user |
217
|
|
|
* |
218
|
|
|
* @access public |
219
|
|
|
* |
220
|
|
|
* @param integer $userId user id |
221
|
|
|
* @param string $name element name |
222
|
|
|
* |
223
|
|
|
* @return bool |
224
|
|
|
*/ |
225
|
|
|
public function revoke($userId, $name) |
226
|
|
|
{ |
227
|
|
|
return $this->db->delete('rbac_user', 'name=:name AND user=:user', |
228
|
|
|
['name' => $name, 'user' => $userId]); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.