1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\User; |
4
|
|
|
|
5
|
|
|
use InvalidArgumentException; |
6
|
|
|
|
7
|
|
|
// From PSR-3 |
8
|
|
|
use Psr\Log\LoggerAwareInterface; |
9
|
|
|
use Psr\Log\LoggerAwareTrait; |
10
|
|
|
|
11
|
|
|
// From 'laminas/laminas-permissions-acl' |
12
|
|
|
use Laminas\Permissions\Acl\Acl; |
13
|
|
|
use Laminas\Permissions\Acl\Exception\ExceptionInterface as AclExceptionInterface; |
14
|
|
|
use Laminas\Permissions\Acl\Resource\ResourceInterface as AclResourceInterface; |
15
|
|
|
use Laminas\Permissions\Acl\Role\RoleInterface as AclRoleInterface; |
16
|
|
|
|
17
|
|
|
// From 'charcoal-user' |
18
|
|
|
use Charcoal\User\UserInterface; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* The base Authorizer service |
22
|
|
|
* |
23
|
|
|
* The authorizer service helps with user authorization (permission checking). |
24
|
|
|
* |
25
|
|
|
* ## Constructor dependencies |
26
|
|
|
* |
27
|
|
|
* Constructor dependencies are passed as an array of `key => value` pair. |
28
|
|
|
* The required dependencies are: |
29
|
|
|
* |
30
|
|
|
* - `logger` A PSR3 logger instance. |
31
|
|
|
* - `acl` A Laminas ACL (Access-Control-List) instance. |
32
|
|
|
* |
33
|
|
|
* ## Checking permissions |
34
|
|
|
* |
35
|
|
|
* To check if a given ACL (passed in constructor) allows a list of permissions (aka privileges): |
36
|
|
|
* |
37
|
|
|
* - `xxx(UserInterface $user, string[] $aclPermissions)` |
38
|
|
|
*/ |
39
|
|
|
abstract class AbstractAuthorizer implements |
40
|
|
|
AuthorizerInterface, |
41
|
|
|
LoggerAwareInterface |
42
|
|
|
{ |
43
|
|
|
use LoggerAwareTrait; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* The ACL service. |
47
|
|
|
* |
48
|
|
|
* @var Acl |
49
|
|
|
*/ |
50
|
|
|
private $acl; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @param array $data Class dependencies. |
54
|
|
|
*/ |
55
|
|
|
public function __construct(array $data) |
56
|
|
|
{ |
57
|
|
|
$this->setLogger($data['logger']); |
58
|
|
|
$this->setAcl($data['acl']); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Check if access is granted to the role for all permissions. |
63
|
|
|
* |
64
|
|
|
* @param AclRoleInterface|string $role One ACL role to check. |
65
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
66
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
67
|
|
|
* @return boolean|null Returns TRUE if and only if all the $privileges are granted against the $role. |
68
|
|
|
* Returns NULL if no applicable role, resource, or permissions could be checked. |
69
|
|
|
*/ |
70
|
|
View Code Duplication |
public function isRoleGrantedAll($role, $resource, $privileges) |
|
|
|
|
71
|
|
|
{ |
72
|
|
|
$privileges = (array)$privileges; |
73
|
|
|
$result = null; |
74
|
|
|
|
75
|
|
|
try { |
76
|
|
|
foreach ($privileges as $privilege) { |
77
|
|
|
if (!$this->isAllowed($role, $resource, $privilege)) { |
78
|
|
|
return false; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
$result = true; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
return $result; |
85
|
|
|
} catch (AclExceptionInterface $e) { |
86
|
|
|
$this->logger->error('[ACL] '.$e->getMessage()); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
return null; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Check if access is granted to all roles for all permissions. |
94
|
|
|
* |
95
|
|
|
* @param AclRoleInterface|string|array $roles One or many ACL roles to check. |
96
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
97
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
98
|
|
|
* @return boolean|null Returns TRUE if and only if all the $privileges are granted against all $roles. |
99
|
|
|
* Returns NULL if no applicable roles, resource, or permissions could be checked. |
100
|
|
|
*/ |
101
|
|
View Code Duplication |
public function allRolesGrantedAll($roles, $resource, $privileges) |
|
|
|
|
102
|
|
|
{ |
103
|
|
|
$roles = (array)$roles; |
104
|
|
|
$privileges = (array)$privileges; |
105
|
|
|
$result = null; |
106
|
|
|
|
107
|
|
|
foreach ($roles as $role) { |
108
|
|
|
if (!$this->isRoleGrantedAll($role, $resource, $privileges)) { |
109
|
|
|
return false; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
$result = true; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
return $result; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Check if access is granted to any one of the roles for all permissions. |
120
|
|
|
* |
121
|
|
|
* @param AclRoleInterface|string|array $roles One or many ACL roles to check. |
122
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
123
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
124
|
|
|
* @return boolean|null Returns TRUE if and only if all the $privileges are granted against any one of the $roles. |
125
|
|
|
* Returns NULL if no applicable roles, resource, or permissions could be checked. |
126
|
|
|
*/ |
127
|
|
|
public function anyRolesGrantedAll($roles, $resource, $privileges) |
128
|
|
|
{ |
129
|
|
|
$roles = (array)$roles; |
130
|
|
|
$privileges = (array)$privileges; |
131
|
|
|
$result = null; |
132
|
|
|
|
133
|
|
|
foreach ($roles as $role) { |
134
|
|
|
if ($this->isRoleGrantedAll($role, $resource, $privileges)) { |
135
|
|
|
return true; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$result = false; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return $result; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Check if access is granted to the role for any one of the permissions. |
146
|
|
|
* |
147
|
|
|
* @param AclRoleInterface|string $role One ACL role to check. |
148
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
149
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
150
|
|
|
* @return boolean|null Returns TRUE if any one of the $privileges are granted against the $role. |
151
|
|
|
* Returns NULL if no applicable role, resource, or permissions could be checked. |
152
|
|
|
*/ |
153
|
|
View Code Duplication |
public function isRoleGrantedAny($role, $resource, $privileges) |
|
|
|
|
154
|
|
|
{ |
155
|
|
|
$privileges = (array)$privileges; |
156
|
|
|
$result = null; |
157
|
|
|
|
158
|
|
|
try { |
159
|
|
|
foreach ($privileges as $privilege) { |
160
|
|
|
if ($this->isAllowed($role, $resource, $privilege)) { |
161
|
|
|
return true; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$result = false; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
return $result; |
168
|
|
|
} catch (AclExceptionInterface $e) { |
169
|
|
|
$this->logger->error('[ACL] '.$e->getMessage()); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
return null; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Check if access is granted to all roles for any one of the permissions. |
177
|
|
|
* |
178
|
|
|
* @param AclRoleInterface|string|array $roles One or many ACL roles to check. |
179
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
180
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
181
|
|
|
* @return boolean|null Returns TRUE if any one of the $privileges are granted against all $roles. |
182
|
|
|
* Returns NULL if no applicable roles, resource, or permissions could be checked. |
183
|
|
|
*/ |
184
|
|
View Code Duplication |
public function allRolesGrantedAny($roles, $resource, $privileges) |
|
|
|
|
185
|
|
|
{ |
186
|
|
|
$roles = (array)$roles; |
187
|
|
|
$privileges = (array)$privileges; |
188
|
|
|
$result = null; |
189
|
|
|
|
190
|
|
|
foreach ($roles as $role) { |
191
|
|
|
if (!$this->isRoleGrantedAny($role, $resource, $privileges)) { |
192
|
|
|
return false; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$result = true; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
return $result; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Check if access is granted to any one of the roles for any one of the permissions. |
203
|
|
|
* |
204
|
|
|
* @param AclRoleInterface|string|array $roles One or many ACL roles to check. |
205
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
206
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
207
|
|
|
* @return boolean|null |
208
|
|
|
* Returns TRUE if any one of the $privileges are granted against any one of the $roles. |
209
|
|
|
* Returns NULL if no applicable roles, resource, or permissions could be checked. |
210
|
|
|
*/ |
211
|
|
|
public function anyRolesGrantedAny($roles, $resource, $privileges) |
212
|
|
|
{ |
213
|
|
|
$roles = (array)$roles; |
214
|
|
|
$privileges = (array)$privileges; |
215
|
|
|
$result = null; |
216
|
|
|
|
217
|
|
|
foreach ($roles as $role) { |
218
|
|
|
if ($this->isRoleGrantedAny($role, $resource, $privileges)) { |
219
|
|
|
return true; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
$result = false; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
return $result; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Check if access is granted to the user's role(s) for permissions. |
230
|
|
|
* |
231
|
|
|
* @param UserInterface $user The user to check. |
232
|
|
|
* @param AclResourceInterface|string|null $resource One ACL resource to check. |
233
|
|
|
* @param string|string[] $privileges One or many ACL privileges to check. |
234
|
|
|
* @return boolean|null |
235
|
|
|
* Returns TRUE if and only if the $privileges are granted against one of the roles of the $user. |
236
|
|
|
* Returns NULL if no applicable roles, resource, or permissions could be checked. |
237
|
|
|
*/ |
238
|
|
|
public function isUserGranted(UserInterface $user, $resource, $privileges) |
239
|
|
|
{ |
240
|
|
|
return $this->anyRolesGrantedAll($user['roles'], $resource, $privileges); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
|
244
|
|
|
|
245
|
|
|
// Helpers from \Laminas\Permissions\Acl\Acl |
246
|
|
|
// ========================================================================= |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Check if the role has access to the resource and privilege. |
250
|
|
|
* |
251
|
|
|
* This method is a proxy to {@see \Laminas\Permissions\Acl\Acl::isAllowed()}. |
252
|
|
|
* |
253
|
|
|
* @param AclRoleInterface|string $role The ACL role to check. |
254
|
|
|
* If $role is NULL, then the ACL will check for a "blacklist" rule |
255
|
|
|
* (allow everything to all). |
256
|
|
|
* @param AclResourceInterface|string $resource The ACL resource to check. |
257
|
|
|
* If $resource is NULL, then the ACL will check for a "blacklist" rule |
258
|
|
|
* (allow everything to all). |
259
|
|
|
* @param string $privilege The ACL privilege to check. |
260
|
|
|
* If $privilege is NULL, then the ACL returns TRUE if and only if |
261
|
|
|
* the $role is allowed all privileges on the $resource. |
262
|
|
|
* @return boolean Returns TRUE if and only if the $role has access to the $resource. |
263
|
|
|
*/ |
264
|
|
|
public function isAllowed($role = null, $resource = null, $privilege = null) |
265
|
|
|
{ |
266
|
|
|
return $this->getAcl()->isAllowed($role, $resource, $privilege); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Determine if the role is registered. |
271
|
|
|
* |
272
|
|
|
* @see \Laminas\Permissions\Acl\Acl::hasRole() |
273
|
|
|
* |
274
|
|
|
* @param RoleInterface|string $role The ACL role to check. |
275
|
|
|
* @return boolean Returns TRUE if and only if the $role exists in the registry. |
276
|
|
|
*/ |
277
|
|
|
public function hasRole($role) |
278
|
|
|
{ |
279
|
|
|
return $this->getAcl()->hasRole($role); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Determine if the role inherits from another role. |
284
|
|
|
* |
285
|
|
|
* @see \Laminas\Permissions\Acl\Acl::inheritsRole() |
286
|
|
|
* |
287
|
|
|
* @param RoleInterface|string $role The ACL role to check. |
288
|
|
|
* @param RoleInterface|string $inherit The ACL role to check $role against. |
289
|
|
|
* @param boolean $onlyParents Whether the $role must inherit directly from $inherit. |
290
|
|
|
* @return boolean Returns TRUE if and only if $role inherits from $inherit. |
291
|
|
|
*/ |
292
|
|
|
public function inheritsRole($role, $inherit, $onlyParents = false) |
293
|
|
|
{ |
294
|
|
|
return $this->getAcl()->inheritsRole($role, $inherit, $onlyParents); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Determine if the resource is registered. |
299
|
|
|
* |
300
|
|
|
* @see \Laminas\Permissions\Acl\Acl::hasResource() |
301
|
|
|
* |
302
|
|
|
* @param AclResourceInterface|string $resource The ACL resource to check. |
303
|
|
|
* @return boolean Returns TRUE if and only if the $resource exists in the ACL. |
304
|
|
|
*/ |
305
|
|
|
public function hasResource($resource) |
306
|
|
|
{ |
307
|
|
|
return $this->getAcl()->hasResource($resource); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Determine if the resource inherits from another resource. |
312
|
|
|
* |
313
|
|
|
* @see \Laminas\Permissions\Acl\Acl::inheritsResource() |
314
|
|
|
* |
315
|
|
|
* @param AclResourceInterface|string $resource The ACL resource to check. |
316
|
|
|
* @param AclResourceInterface|string $inherit The ACL resource to check $resource against. |
317
|
|
|
* @param boolean $onlyParent Whether the $resource must inherit directly from $inherit. |
318
|
|
|
* @return boolean Returns TRUE if and only if $resource inherits from $inherit. |
319
|
|
|
*/ |
320
|
|
|
public function inheritsResource($resource, $inherit, $onlyParent = false) |
321
|
|
|
{ |
322
|
|
|
return $this->getAcl()->inheritsResource($resource, $inherit, $onlyParent); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
|
326
|
|
|
|
327
|
|
|
// Dependencies |
328
|
|
|
// ========================================================================= |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* @return Acl |
332
|
|
|
*/ |
333
|
|
|
protected function getAcl() |
334
|
|
|
{ |
335
|
|
|
return $this->acl; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* @param Acl $acl The ACL service. |
340
|
|
|
* @return void |
341
|
|
|
*/ |
342
|
|
|
private function setAcl(Acl $acl) |
343
|
|
|
{ |
344
|
|
|
$this->acl = $acl; |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
|
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.