/
AuthRbac.php
230 lines (203 loc) · 6.54 KB
/
AuthRbac.php
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
<?php
namespace li3_access\extensions\adapter\security\access;
use lithium\security\Auth;
use lithium\core\ConfigException;
use lithium\util\Inflector;
class AuthRbac extends \lithium\core\Object {
/**
* @var array $_autoConfig
* @see lithium\core\Object::$_autoConfig
*/
protected $_autoConfig = array('roles');
/**
* @var mixed $_roles null if unset array otherwise with fetched AuthObjects
*/
protected $_roles = null;
/**
* The `Rbac` adapter will iterate trough the rbac data Array.
*
* @todo: write better tests!
*
* @param mixed $requester The user data array that holds all necessary information about
* the user requesting access. Or false (because Auth::check() can return false).
* This is an optional parameter, bercause we will fetch the users data trough Auth
* seperately.
* @param mixed $params The Lithium `Request` object, or an array with at least
* 'request', and 'params'
* @param array $options An array of additional options for the _getRolesByAuth method.
* @return Array An empty array if access is allowed or
* an array with reasons for denial if denied.
*/
public function check($requester, $params, array $options = array()) {
if (empty($this->_roles)) {
throw new ConfigException('No roles defined for adapter configuration.');
}
$roleDefaults = array(
'message' => '',
'redirect' => '',
'allow' => true,
'requesters' => '*',
'match' => '*::*'
);
$message = $options['message'];
$redirect = $options['redirect'];
$accessible = false;
foreach ($this->_roles as $role) {
$role += $roleDefaults;
if (is_callable($role['allow'])) {
$role['allow'] = (array) $role['allow'];
}
// Check to see if this role applies to this request
if (!static::parseMatch($role['match'], $params)) {
continue;
}
$accessible = static::_isAccessible($role, $params, $options);
if (!$accessible) {
$message = !empty($role['message']) ? $role['message'] : $message;
$redirect = !empty($role['redirect']) ? $role['redirect'] : $redirect;
}
}
return !$accessible ? compact('message', 'redirect') : array();
}
/**
* Checks if the Role grants access
* If allow === false => no access
* If requesters has no role => no access
* If allows contains closures => return closures return
* Otherwise => grants access
*
* @param array $role Array Set of Roles (dereferenced)
* @param mixed $quest A lithium Request object.
* @param array $options An array of additional options for the _getRolesByAuth method.
* @return boolean $accessable
*/
protected static function _isAccessible(&$role, $params, $options) {
if ($role['allow'] === false) {
return false;
}
if (!static::_hasRole($role['requesters'], $params, $options)) {
return false;
}
if (is_array($role['allow'])) {
return static::_parseClosures($role['allow'], $params['request'], $role);
}
return true;
}
/**
* parseMatch Matches the current request parameters against a set of given parameters.
* Can match against a shorthand string (Controller::action) or a full array. If a parameter
* is provided then it must have an equivilent in the Request objects parmeters in order
* to validate. * Is also acceptable to match a parameter without a specific value.
*
* @param mixed $match A set of parameters to validate the request against.
* @param mixed $params The Lithium `Request` object, or an array with at least
* 'request', and 'params'
* @access public
* @return boolean True if a match is found.
*/
public static function parseMatch($match, $params) {
if (empty($match)) {
return false;
}
if (is_array($match)) {
$_params = $params;
if (!static::_parseClosures($match, $params['request'], $_params)) {
return false;
}
} elseif (is_callable($match)) {
return (boolean) $match($params['request'], $params);
}
$matchParams = array();
foreach ((array) $match as $key => $param) {
if (is_string($param)) {
if (preg_match('/^([A-Za-z0-9_\*\\\]+)::([A-Za-z0-9_\*]+)$/', $param, $regexMatches)) {
$matchParams += array(
'controller' => $regexMatches[1],
'action' => $regexMatches[2]
);
continue;
}
}
$matchParams[$key] = $param;
}
foreach ($matchParams as $type => $value) {
if ($value === '*') {
continue;
}
if ($type === 'controller') {
$value = Inflector::underscore($value);
}
$exists_in_request = array_key_exists($type, $params['params']);
if (!$exists_in_request || $value !== Inflector::underscore($params['params'][$type])) {
return false;
}
}
return true;
}
/**
* _parseClosures Iterates over an array and runs any anonymous functions it
* finds. Returns true if all of the closures it runs evaluate to true. $match
* is passed by refference and any closures found are removed from it before the
* method is complete.
*
* @static
* @access protected
*
* @param array $data dereferenced Array
* @param object $request The Lithium `Request` object
* @param array $roleOptions dereferenced Array
* @return boolean
*/
protected static function _parseClosures(array &$data, $request, array &$roleOptions = array()) {
$return = true;
foreach ($data as $key => $item) {
if (is_callable($item)) {
if ($return === true) {
$return = (boolean) $item($request, $roleOptions);
}
unset($data[$key]);
}
}
return $return;
}
/**
* @todo reduce Model Overhead (will duplicated in each model)
*
* @param mixed $params The Lithium `Request` object, or an array with at least
* 'request', and 'params'
* @param array $options
* @return array|mixed $roles Roles with attachted User Models
*/
protected static function _getRolesByAuth($params, array $options = array()) {
$roles = array('*' => '*');
foreach (array_keys(Auth::config()) as $key) {
if ($check = Auth::check($key, $params['request'], $options)) {
$roles[$key] = $check;
}
}
return $roles = array_filter($roles);
}
/**
* _hasRole Compares the results from _getRolesByAuth with the array passed to it.
*
* @param mixed $requesters
* @param mixed $params
* @param array $options
* @access protected
* @return void
*/
protected static function _hasRole($requesters, $params, array $options = array()) {
$authed = array_keys(static::_getRolesByAuth($params, $options));
$requesters = (array) $requesters;
if (in_array('*', $requesters)) {
return true;
}
foreach ($requesters as $requester) {
if (in_array($requester, $authed)) {
return true;
}
}
return false;
}
}
?>