1
|
|
|
<?php
|
|
|
|
|
2
|
|
|
declare(strict_types=1);
|
3
|
|
|
|
4
|
|
|
namespace Gewaer\Acl;
|
5
|
|
|
|
6
|
|
|
use Phalcon\Db;
|
7
|
|
|
use Phalcon\Db\AdapterInterface as DbAdapter;
|
8
|
|
|
use Phalcon\Acl\Exception;
|
9
|
|
|
use Phalcon\Acl\Resource;
|
10
|
|
|
use Phalcon\Acl;
|
11
|
|
|
use Phalcon\Acl\Role;
|
12
|
|
|
use Phalcon\Acl\RoleInterface;
|
13
|
|
|
use Gewaer\Models\Companies;
|
14
|
|
|
use Gewaer\Models\Apps;
|
15
|
|
|
use Phalcon\Acl\Adapter\Database as PhalconAclDatabaseAdapter;
|
16
|
|
|
use Phalcon\Acl\Adapter;
|
17
|
|
|
use BadMethodCallException;
|
18
|
|
|
|
19
|
|
|
/**
|
20
|
|
|
* Phalcon\Acl\Adapter\Database
|
21
|
|
|
* Manages Geweaer Multi tenant ACL lists in database
|
22
|
|
|
*
|
23
|
|
|
* #extends PhalconAclDatabaseAdapter #had to comments it out testing breaking
|
24
|
|
|
*/
|
25
|
|
|
class Manager extends Adapter
|
26
|
|
|
{
|
27
|
|
|
/**
|
28
|
|
|
* @var DbAdapter
|
29
|
|
|
*/
|
30
|
|
|
protected $connection;
|
31
|
|
|
|
32
|
|
|
/**
|
33
|
|
|
* Roles table
|
34
|
|
|
* @var string
|
35
|
|
|
*/
|
36
|
|
|
protected $roles;
|
37
|
|
|
|
38
|
|
|
/**
|
39
|
|
|
* Resources table
|
40
|
|
|
* @var string
|
41
|
|
|
*/
|
42
|
|
|
protected $resources;
|
43
|
|
|
|
44
|
|
|
/**
|
45
|
|
|
* Resources Accesses table
|
46
|
|
|
* @var string
|
47
|
|
|
*/
|
48
|
|
|
protected $resourcesAccesses;
|
49
|
|
|
|
50
|
|
|
/**
|
51
|
|
|
* Access List table
|
52
|
|
|
* @var string
|
53
|
|
|
*/
|
54
|
|
|
protected $accessList;
|
55
|
|
|
|
56
|
|
|
/**
|
57
|
|
|
* Roles Inherits table
|
58
|
|
|
* @var string
|
59
|
|
|
*/
|
60
|
|
|
protected $rolesInherits;
|
61
|
|
|
|
62
|
|
|
/**
|
63
|
|
|
* Default action for no arguments is allow
|
64
|
|
|
* @var int
|
65
|
|
|
*/
|
66
|
|
|
protected $noArgumentsDefaultAction = Acl::ALLOW;
|
67
|
|
|
|
68
|
|
|
/**
|
69
|
|
|
* Company Object
|
70
|
|
|
*
|
71
|
|
|
* @var Companies
|
72
|
|
|
*/
|
73
|
|
|
protected $company;
|
74
|
|
|
|
75
|
|
|
/**
|
76
|
|
|
* App Objc
|
77
|
|
|
*
|
78
|
|
|
* @var Apps
|
79
|
|
|
*/
|
80
|
|
|
protected $app;
|
81
|
|
|
|
82
|
|
|
/**
|
83
|
|
|
* Class constructor.
|
84
|
|
|
*
|
85
|
|
|
* @param array $options Adapter config
|
86
|
|
|
* @throws Exception
|
87
|
|
|
*/
|
88
|
12 |
|
public function __construct(array $options)
|
89
|
|
|
{
|
90
|
12 |
|
if (!isset($options['db']) || !$options['db'] instanceof DbAdapter) {
|
91
|
|
|
throw new Exception(
|
92
|
|
|
'Parameter "db" is required and it must be an instance of Phalcon\Acl\AdapterInterface'
|
93
|
|
|
);
|
94
|
|
|
}
|
95
|
|
|
|
96
|
12 |
|
$this->connection = $options['db'];
|
97
|
|
|
|
98
|
12 |
|
foreach (['roles', 'resources', 'resourcesAccesses', 'accessList', 'rolesInherits'] as $table) {
|
99
|
12 |
|
if (!isset($options[$table]) || empty($options[$table]) || !is_string($options[$table])) {
|
100
|
|
|
throw new Exception("Parameter '{$table}' is required and it must be a non empty string");
|
101
|
|
|
}
|
102
|
|
|
|
103
|
12 |
|
$this->{$table} = $this->connection->escapeIdentifier($options[$table]);
|
104
|
|
|
}
|
105
|
12 |
|
}
|
106
|
|
|
|
107
|
|
|
/**
|
108
|
|
|
* Set current user Company
|
109
|
|
|
*
|
110
|
|
|
* @param Companies $company
|
111
|
|
|
* @return void
|
112
|
|
|
*/
|
113
|
|
|
public function setCompany(Companies $company): void
|
114
|
|
|
{
|
115
|
|
|
$this->company = $company;
|
116
|
|
|
}
|
117
|
|
|
|
118
|
|
|
/**
|
119
|
|
|
* Set current user app
|
120
|
|
|
*
|
121
|
|
|
* @param Apps $app
|
122
|
|
|
* @return void
|
123
|
|
|
*/
|
124
|
6 |
|
public function setApp(Apps $app): void
|
125
|
|
|
{
|
126
|
6 |
|
$this->app = $app;
|
127
|
6 |
|
}
|
128
|
|
|
|
129
|
|
|
/**
|
130
|
|
|
* Get the current App
|
131
|
|
|
*
|
132
|
|
|
* @return void
|
133
|
|
|
*/
|
134
|
7 |
|
public function getApp(): Apps
|
135
|
|
|
{
|
136
|
7 |
|
if (!is_object($this->app)) {
|
137
|
2 |
|
$this->app = new Apps();
|
138
|
2 |
|
$this->app->id = 0;
|
139
|
2 |
|
$this->app->name = 'Canvas';
|
140
|
|
|
}
|
141
|
|
|
|
142
|
7 |
|
return $this->app;
|
|
|
|
|
143
|
|
|
}
|
144
|
|
|
|
145
|
|
|
/**
|
146
|
|
|
* Get the current App
|
147
|
|
|
*
|
148
|
|
|
* @return void
|
149
|
|
|
*/
|
150
|
|
|
public function getCompany() : Companies
|
151
|
|
|
{
|
152
|
|
|
if (!is_object($this->company)) {
|
153
|
|
|
$this->company = new Companies();
|
154
|
|
|
$this->company->id = 0;
|
155
|
|
|
$this->company->name = 'Canvas';
|
156
|
|
|
}
|
157
|
|
|
|
158
|
|
|
return $this->company;
|
|
|
|
|
159
|
|
|
}
|
160
|
|
|
|
161
|
|
|
/**
|
162
|
|
|
* {@inheritdoc}
|
163
|
|
|
*
|
164
|
|
|
* Example:
|
165
|
|
|
* <code>
|
166
|
|
|
* $acl->addRole(new Phalcon\Acl\Role('administrator'), 'consultor');
|
167
|
|
|
* $acl->addRole('administrator', 'consultor');
|
168
|
|
|
* </code>
|
169
|
|
|
*
|
170
|
|
|
* @param \Phalcon\Acl\Role|string $role
|
171
|
|
|
* @param int $scope
|
172
|
|
|
* @param string $accessInherits
|
173
|
|
|
* @return boolean
|
174
|
|
|
* @throws \Phalcon\Acl\Exception
|
175
|
|
|
*/
|
176
|
2 |
|
public function addRole($role, $scope = 0, $accessInherits = null): bool
|
177
|
|
|
{
|
178
|
2 |
|
if (is_string($role)) {
|
179
|
1 |
|
$role = $this->setAppByRole($role);
|
180
|
|
|
|
181
|
1 |
|
$role = new Role($role, ucwords($role) . ' Role');
|
182
|
|
|
}
|
183
|
2 |
|
if (!$role instanceof RoleInterface) {
|
|
|
|
|
184
|
|
|
throw new Exception('Role must be either an string or implement RoleInterface');
|
185
|
|
|
}
|
186
|
|
|
|
187
|
2 |
|
$exists = $this->connection->fetchOne(
|
188
|
2 |
|
"SELECT COUNT(*) FROM {$this->roles} WHERE name = ?",
|
189
|
2 |
|
null,
|
190
|
2 |
|
[$role->getName()]
|
|
|
|
|
191
|
|
|
);
|
192
|
|
|
|
193
|
2 |
|
if (!$exists[0]) {
|
194
|
|
|
$this->connection->execute(
|
195
|
|
|
"INSERT INTO {$this->roles} (name, description, company_id, apps_id, scope, created_at) VALUES (?, ?, ?, ?, ?, ?)",
|
196
|
|
|
[$role->getName(), $role->getDescription(), $this->getCompany()->getId(), $this->getApp()->getId(), $scope, date('Y-m-d H:i:s')]
|
197
|
|
|
);
|
198
|
|
|
$this->connection->execute(
|
199
|
|
|
"INSERT INTO {$this->accessList} (roles_name, resources_name, access_name, allowed, apps_id, created_at) VALUES (?, ?, ?, ?, ?, ?)",
|
200
|
|
|
[$role->getName(), '*', '*', $this->_defaultAccess, $this->getApp()->getId(), date('Y-m-d H:i:s')]
|
201
|
|
|
);
|
202
|
|
|
}
|
203
|
2 |
|
if ($accessInherits) {
|
204
|
|
|
return $this->addInherit($role->getName(), $accessInherits);
|
205
|
|
|
}
|
206
|
2 |
|
return true;
|
207
|
|
|
}
|
208
|
|
|
|
209
|
|
|
/**
|
210
|
|
|
* {@inheritdoc}
|
211
|
|
|
*
|
212
|
|
|
* @param string $roleName
|
213
|
|
|
* @param string $roleToInherit
|
214
|
|
|
* @throws \Phalcon\Acl\Exception
|
215
|
|
|
*/
|
216
|
|
|
public function addInherit($roleName, $roleToInherit): bool
|
217
|
|
|
{
|
218
|
|
|
$sql = "SELECT COUNT(*) FROM {$this->roles} WHERE name = ?";
|
219
|
|
|
$exists = $this->connection->fetchOne($sql, null, [$roleName]);
|
|
|
|
|
220
|
|
|
if (!$exists[0]) {
|
221
|
|
|
throw new Exception("Role '{$roleName}' does not exist in the role list");
|
222
|
|
|
}
|
223
|
|
|
$exists = $this->connection->fetchOne(
|
224
|
|
|
"SELECT COUNT(*) FROM {$this->rolesInherits} WHERE roles_name = ? AND roles_inherit = ?",
|
225
|
|
|
null,
|
226
|
|
|
[$roleName, $roleToInherit]
|
|
|
|
|
227
|
|
|
);
|
228
|
|
|
if (!$exists[0]) {
|
229
|
|
|
$this->connection->execute(
|
230
|
|
|
"INSERT INTO {$this->rolesInherits} VALUES (?, ?)",
|
231
|
|
|
[$roleName, $roleToInherit]
|
232
|
|
|
);
|
|
|
|
|
233
|
|
|
}
|
234
|
|
|
}
|
235
|
|
|
|
236
|
|
|
/**
|
237
|
|
|
* {@inheritdoc}
|
238
|
|
|
*
|
239
|
|
|
* @param string $roleName
|
240
|
|
|
* @return boolean
|
241
|
|
|
*/
|
242
|
2 |
|
public function isRole($roleName): bool
|
243
|
|
|
{
|
244
|
2 |
|
$exists = $this->connection->fetchOne(
|
245
|
2 |
|
"SELECT COUNT(*) FROM {$this->roles} WHERE name = ?",
|
246
|
2 |
|
null,
|
247
|
2 |
|
[$roleName]
|
|
|
|
|
248
|
|
|
);
|
249
|
2 |
|
return (bool) $exists[0];
|
250
|
|
|
}
|
251
|
|
|
|
252
|
|
|
/**
|
253
|
|
|
* {@inheritdoc}
|
254
|
|
|
*
|
255
|
|
|
* @param string $resourceName
|
256
|
|
|
* @return boolean
|
257
|
|
|
*/
|
258
|
1 |
|
public function isResource($resourceName): bool
|
259
|
|
|
{
|
260
|
1 |
|
$exists = $this->connection->fetchOne(
|
261
|
1 |
|
"SELECT COUNT(*) FROM {$this->resources} WHERE name = ?",
|
262
|
1 |
|
null,
|
263
|
1 |
|
[$resourceName]
|
|
|
|
|
264
|
|
|
);
|
265
|
1 |
|
return (bool) $exists[0];
|
266
|
|
|
}
|
267
|
|
|
|
268
|
|
|
/**
|
269
|
|
|
* Given a resource with a dot CRM.Leads , it will set the app
|
270
|
|
|
*
|
271
|
|
|
* @param string $resource
|
272
|
|
|
* @return void
|
273
|
|
|
*/
|
274
|
7 |
|
protected function setAppByResource(string $resource): string
|
275
|
|
|
{
|
276
|
|
|
//echeck if we have a dot , taht means we are sending the specific app to use
|
277
|
7 |
|
if (strpos($resource, '.') !== false) {
|
278
|
5 |
|
$appResource = explode('.', $resource);
|
279
|
5 |
|
$resource = $appResource[1];
|
280
|
5 |
|
$appName = $appResource[0];
|
281
|
|
|
|
282
|
|
|
//look for the app and set it
|
283
|
5 |
|
if ($app = Apps::getACLApp($appName)) {
|
284
|
5 |
|
$this->setApp($app);
|
285
|
|
|
}
|
286
|
|
|
}
|
287
|
|
|
|
288
|
7 |
|
return $resource;
|
|
|
|
|
289
|
|
|
}
|
290
|
|
|
|
291
|
|
|
/**
|
292
|
|
|
* Given a resource with a dot CRM.Leads , it will set the app
|
293
|
|
|
*
|
294
|
|
|
* @param string $resource
|
295
|
|
|
* @return void
|
296
|
|
|
*/
|
297
|
5 |
|
protected function setAppByRole(string $role) : string
|
298
|
|
|
{
|
299
|
|
|
//echeck if we have a dot , taht means we are sending the specific app to use
|
300
|
5 |
|
if (strpos($role, '.') !== false) {
|
301
|
1 |
|
$appRole = explode('.', $role);
|
302
|
1 |
|
$role = $appRole[1];
|
303
|
1 |
|
$appName = $appRole[0];
|
304
|
|
|
|
305
|
|
|
//look for the app and set it
|
306
|
1 |
|
if ($app = Apps::getACLApp($appName)) {
|
307
|
1 |
|
$this->setApp($app);
|
308
|
|
|
}
|
309
|
|
|
}
|
310
|
|
|
|
311
|
5 |
|
return $role;
|
|
|
|
|
312
|
|
|
}
|
313
|
|
|
|
314
|
|
|
/**
|
315
|
|
|
* {@inheritdoc}
|
316
|
|
|
* Example:
|
317
|
|
|
* <code>
|
318
|
|
|
* //Add a resource to the the list allowing access to an action
|
319
|
|
|
* $acl->addResource(new Phalcon\Acl\Resource('customers'), 'search');
|
320
|
|
|
* $acl->addResource('customers', 'search');
|
321
|
|
|
* //Add a resource with an access list
|
322
|
|
|
* $acl->addResource(new Phalcon\Acl\Resource('customers'), ['create', 'search']);
|
323
|
|
|
* $acl->addResource('customers', ['create', 'search']);
|
324
|
|
|
* $acl->addResource('App.customers', ['create', 'search']);
|
325
|
|
|
* </code>
|
326
|
|
|
*
|
327
|
|
|
* @param \Phalcon\Acl\Resource|string $resource
|
328
|
|
|
* @param array|string $accessList
|
329
|
|
|
* @return boolean
|
330
|
|
|
*/
|
331
|
1 |
|
public function addResource($resource, $accessList = null): bool
|
332
|
|
|
{
|
333
|
1 |
|
if (!is_object($resource)) {
|
334
|
|
|
//echeck if we have a dot , taht means we are sending the specific app to use
|
335
|
1 |
|
$resource = $this->setAppByResource($resource);
|
336
|
|
|
|
337
|
1 |
|
$resource = new Resource($resource);
|
338
|
|
|
}
|
339
|
|
|
|
340
|
1 |
|
$exists = $this->connection->fetchOne(
|
341
|
1 |
|
"SELECT COUNT(*) FROM {$this->resources} WHERE name = ?",
|
342
|
1 |
|
null,
|
343
|
1 |
|
[$resource->getName()]
|
|
|
|
|
344
|
|
|
);
|
345
|
|
|
|
346
|
1 |
|
if (!$exists[0]) {
|
347
|
1 |
|
$this->connection->execute(
|
348
|
1 |
|
"INSERT INTO {$this->resources} (name, description, apps_id, created_at) VALUES (?, ?, ?, ?)",
|
349
|
1 |
|
[$resource->getName(), $resource->getDescription(), $this->getApp()->getId(), date('Y-m-d H:i:s')]
|
350
|
|
|
);
|
351
|
|
|
}
|
352
|
|
|
|
353
|
1 |
|
if ($accessList) {
|
354
|
1 |
|
return $this->addResourceAccess($resource->getName(), $accessList);
|
355
|
|
|
}
|
356
|
|
|
|
357
|
|
|
return true;
|
358
|
|
|
}
|
359
|
|
|
|
360
|
|
|
/**
|
361
|
|
|
* {@inheritdoc}
|
362
|
|
|
*
|
363
|
|
|
* @param string $resourceName
|
364
|
|
|
* @param array|string $accessList
|
365
|
|
|
* @return boolean
|
366
|
|
|
* @throws \Phalcon\Acl\Exception
|
367
|
|
|
*/
|
368
|
1 |
|
public function addResourceAccess($resourceName, $accessList): bool
|
369
|
|
|
{
|
370
|
1 |
|
if (!$this->isResource($resourceName)) {
|
371
|
|
|
throw new Exception("Resource '{$resourceName}' does not exist in ACL");
|
372
|
|
|
}
|
373
|
|
|
|
374
|
1 |
|
$sql = "SELECT COUNT(*) FROM {$this->resourcesAccesses} WHERE resources_name = ? AND access_name = ? AND apps_id = ?";
|
375
|
1 |
|
if (!is_array($accessList)) {
|
376
|
|
|
$accessList = [$accessList];
|
377
|
|
|
}
|
378
|
|
|
|
379
|
1 |
|
foreach ($accessList as $accessName) {
|
380
|
1 |
|
$exists = $this->connection->fetchOne($sql, null, [$resourceName, $accessName, $this->getApp()->getId()]);
|
|
|
|
|
381
|
1 |
|
if (!$exists[0]) {
|
382
|
1 |
|
$this->connection->execute(
|
383
|
1 |
|
'INSERT INTO ' . $this->resourcesAccesses . ' (resources_name, access_name, apps_id, created_at) VALUES (?, ?, ?, ?)',
|
384
|
1 |
|
[$resourceName, $accessName, $this->getApp()->getId(), date('Y-m-d H:i:s')]
|
385
|
|
|
);
|
386
|
|
|
}
|
387
|
|
|
}
|
388
|
1 |
|
return true;
|
389
|
|
|
}
|
390
|
|
|
|
391
|
|
|
/**
|
392
|
|
|
* {@inheritdoc}
|
393
|
|
|
*
|
394
|
|
|
* @return \Phalcon\Acl\Resource[]
|
395
|
|
|
*/
|
396
|
|
|
public function getResources(): \Phalcon\Acl\ResourceInterface
|
397
|
|
|
{
|
398
|
|
|
$resources = [];
|
399
|
|
|
$sql = "SELECT * FROM {$this->resources}";
|
400
|
|
|
foreach ($this->connection->fetchAll($sql, Db::FETCH_ASSOC) as $row) {
|
401
|
|
|
$resources[] = new Resource($row['name'], $row['description']);
|
402
|
|
|
}
|
403
|
|
|
return $resources;
|
|
|
|
|
404
|
|
|
}
|
405
|
|
|
|
406
|
|
|
/**
|
407
|
|
|
* {@inheritdoc}
|
408
|
|
|
*
|
409
|
|
|
* @return RoleInterface[]
|
410
|
|
|
*/
|
411
|
|
|
public function getRoles(): \Phalcon\Acl\RoleInterface
|
412
|
|
|
{
|
413
|
|
|
$roles = [];
|
414
|
|
|
$sql = "SELECT * FROM {$this->roles}";
|
415
|
|
|
foreach ($this->connection->fetchAll($sql, Db::FETCH_ASSOC) as $row) {
|
416
|
|
|
$roles[] = new Role($row['name'], $row['description']);
|
417
|
|
|
}
|
418
|
|
|
return $roles;
|
|
|
|
|
419
|
|
|
}
|
420
|
|
|
|
421
|
|
|
/**
|
422
|
|
|
* {@inheritdoc}
|
423
|
|
|
*
|
424
|
|
|
* @param string $resourceName
|
425
|
|
|
* @param array|string $accessList
|
426
|
|
|
*/
|
427
|
|
|
public function dropResourceAccess($resourceName, $accessList)
|
428
|
|
|
{
|
429
|
|
|
throw new BadMethodCallException('Not implemented yet.');
|
430
|
|
|
}
|
431
|
|
|
|
432
|
|
|
/**
|
433
|
|
|
* {@inheritdoc}
|
434
|
|
|
* You can use '*' as wildcard
|
435
|
|
|
* Example:
|
436
|
|
|
* <code>
|
437
|
|
|
* //Allow access to guests to search on customers
|
438
|
|
|
* $acl->allow('guests', 'customers', 'search');
|
439
|
|
|
* //Allow access to guests to search or create on customers
|
440
|
|
|
* $acl->allow('guests', 'customers', ['search', 'create']);
|
441
|
|
|
* //Allow access to any role to browse on products
|
442
|
|
|
* $acl->allow('*', 'products', 'browse');
|
443
|
|
|
* //Allow access to any role to browse on any resource
|
444
|
|
|
* $acl->allow('*', '*', 'browse');
|
445
|
|
|
* </code>
|
446
|
|
|
*
|
447
|
|
|
* @param string $roleName
|
448
|
|
|
* @param string $resourceName
|
449
|
|
|
* @param array|string $access
|
450
|
|
|
* @param mixed $func
|
451
|
|
|
*/
|
452
|
1 |
|
public function allow($roleName, $resourceName, $access, $func = null)
|
453
|
|
|
{
|
454
|
1 |
|
return $this->allowOrDeny($roleName, $resourceName, $access, Acl::ALLOW);
|
455
|
|
|
}
|
456
|
|
|
|
457
|
|
|
/**
|
458
|
|
|
* {@inheritdoc}
|
459
|
|
|
* You can use '*' as wildcard
|
460
|
|
|
* Example:
|
461
|
|
|
* <code>
|
462
|
|
|
* //Deny access to guests to search on customers
|
463
|
|
|
* $acl->deny('guests', 'customers', 'search');
|
464
|
|
|
* //Deny access to guests to search or create on customers
|
465
|
|
|
* $acl->deny('guests', 'customers', ['search', 'create']);
|
466
|
|
|
* //Deny access to any role to browse on products
|
467
|
|
|
* $acl->deny('*', 'products', 'browse');
|
468
|
|
|
* //Deny access to any role to browse on any resource
|
469
|
|
|
* $acl->deny('*', '*', 'browse');
|
470
|
|
|
* </code>
|
471
|
|
|
*
|
472
|
|
|
* @param string $roleName
|
473
|
|
|
* @param string $resourceName
|
474
|
|
|
* @param array|string $access
|
475
|
|
|
* @param mixed $func
|
476
|
|
|
* @return boolean
|
477
|
|
|
*/
|
478
|
1 |
|
public function deny($roleName, $resourceName, $access, $func = null)
|
479
|
|
|
{
|
480
|
1 |
|
return $this->allowOrDeny($roleName, $resourceName, $access, Acl::DENY);
|
481
|
|
|
}
|
482
|
|
|
|
483
|
|
|
/**
|
484
|
|
|
* {@inheritdoc}
|
485
|
|
|
* Example:
|
486
|
|
|
* <code>
|
487
|
|
|
* //Does Andres have access to the customers resource to create?
|
488
|
|
|
* $acl->isAllowed('Andres', 'Products', 'create');
|
489
|
|
|
* //Do guests have access to any resource to edit?
|
490
|
|
|
* $acl->isAllowed('guests', '*', 'edit');
|
491
|
|
|
* </code>
|
492
|
|
|
*
|
493
|
|
|
* @param string $role
|
494
|
|
|
* @param string $resource
|
495
|
|
|
* @param string $access
|
496
|
|
|
* @param array $parameters
|
497
|
|
|
* @return bool
|
498
|
|
|
*/
|
499
|
4 |
|
public function isAllowed($role, $resource, $access, array $parameters = null): bool
|
500
|
|
|
{
|
501
|
4 |
|
$role = $this->setAppByRole($role);
|
502
|
|
|
//resoure always overwrites the role app?
|
503
|
4 |
|
$resource = $this->setAppByResource($resource);
|
504
|
|
|
|
505
|
4 |
|
$sql = implode(' ', [
|
506
|
4 |
|
'SELECT ' . $this->connection->escapeIdentifier('allowed') . " FROM {$this->accessList} AS a",
|
507
|
|
|
// role_name in:
|
508
|
4 |
|
'WHERE roles_name IN (',
|
509
|
|
|
// given 'role'-parameter
|
510
|
4 |
|
'SELECT ? ',
|
511
|
|
|
// inherited role_names
|
512
|
4 |
|
"UNION SELECT roles_inherit FROM {$this->rolesInherits} WHERE roles_name = ?",
|
513
|
|
|
// or 'any'
|
514
|
4 |
|
"UNION SELECT '*'",
|
515
|
4 |
|
')',
|
516
|
|
|
// resources_name should be given one or 'any'
|
517
|
4 |
|
"AND resources_name IN (?, '*')",
|
518
|
|
|
// access_name should be given one or 'any'
|
519
|
|
|
//"AND access_name IN (?, '*')", you need to specify * , we are forcing to check always for permisions
|
520
|
4 |
|
'AND access_name IN (?)',
|
521
|
4 |
|
'AND apps_id = ? ',
|
522
|
|
|
// order be the sum of bools for 'literals' before 'any'
|
523
|
4 |
|
'ORDER BY ' . $this->connection->escapeIdentifier('allowed') . ' DESC',
|
524
|
|
|
// get only one...
|
525
|
4 |
|
'LIMIT 1'
|
526
|
|
|
]);
|
527
|
|
|
|
528
|
|
|
// fetch one entry...
|
529
|
4 |
|
$allowed = $this->connection->fetchOne($sql, Db::FETCH_NUM, [$role, $role, $resource, $access, $this->getApp()->getId()]);
|
|
|
|
|
530
|
|
|
|
531
|
4 |
|
if (is_array($allowed)) {
|
|
|
|
|
532
|
4 |
|
return (bool) $allowed[0];
|
533
|
|
|
}
|
534
|
|
|
|
535
|
|
|
/**
|
536
|
|
|
* Return the default access action
|
537
|
|
|
*/
|
538
|
|
|
return (bool) $this->_defaultAccess;
|
539
|
|
|
}
|
540
|
|
|
|
541
|
|
|
/**
|
542
|
|
|
* Returns the default ACL access level for no arguments provided
|
543
|
|
|
* in isAllowed action if there exists func for accessKey
|
544
|
|
|
*
|
545
|
|
|
* @return int
|
546
|
|
|
*/
|
547
|
|
|
public function getNoArgumentsDefaultAction(): int
|
548
|
|
|
{
|
549
|
|
|
return $this->noArgumentsDefaultAction;
|
550
|
|
|
}
|
551
|
|
|
|
552
|
|
|
/**
|
553
|
|
|
* Sets the default access level for no arguments provided
|
554
|
|
|
* in isAllowed action if there exists func for accessKey
|
555
|
|
|
*
|
556
|
|
|
* @param int $defaultAccess Phalcon\Acl::ALLOW or Phalcon\Acl::DENY
|
557
|
|
|
*/
|
558
|
|
|
public function setNoArgumentsDefaultAction($defaultAccess)
|
559
|
|
|
{
|
560
|
|
|
$this->noArgumentsDefaultAction = intval($defaultAccess);
|
561
|
|
|
}
|
562
|
|
|
|
563
|
|
|
/**
|
564
|
|
|
* Inserts/Updates a permission in the access list
|
565
|
|
|
*
|
566
|
|
|
* @param string $roleName
|
567
|
|
|
* @param string $resourceName
|
568
|
|
|
* @param string $accessName
|
569
|
|
|
* @param integer $action
|
570
|
|
|
* @return boolean
|
571
|
|
|
* @throws \Phalcon\Acl\Exception
|
572
|
|
|
*/
|
573
|
2 |
|
protected function insertOrUpdateAccess($roleName, $resourceName, $accessName, $action)
|
574
|
|
|
{
|
575
|
2 |
|
$resourceName = $this->setAppByResource($resourceName);
|
576
|
|
|
|
577
|
|
|
/**
|
578
|
|
|
* Check if the access is valid in the resource unless wildcard
|
579
|
|
|
*/
|
580
|
2 |
|
if ($resourceName !== '*' && $accessName !== '*') {
|
581
|
2 |
|
$sql = "SELECT COUNT(*) FROM {$this->resourcesAccesses} WHERE resources_name = ? AND access_name = ? and apps_id = ?";
|
582
|
2 |
|
$exists = $this->connection->fetchOne($sql, null, [$resourceName, $accessName, $this->getApp()->getId()]);
|
|
|
|
|
583
|
2 |
|
if (!$exists[0]) {
|
584
|
|
|
throw new Exception(
|
585
|
|
|
"Access '{$accessName}' does not exist in resource '{$resourceName}' in ACL"
|
586
|
|
|
);
|
587
|
|
|
}
|
588
|
|
|
}
|
589
|
|
|
/**
|
590
|
|
|
* Update the access in access_list
|
591
|
|
|
*/
|
592
|
2 |
|
$sql = "SELECT COUNT(*) FROM {$this->accessList} "
|
593
|
2 |
|
. ' WHERE roles_name = ? AND resources_name = ? AND access_name = ? AND apps_id = ?';
|
594
|
2 |
|
$exists = $this->connection->fetchOne($sql, null, [$roleName, $resourceName, $accessName, $this->getApp()->getId()]);
|
|
|
|
|
595
|
2 |
|
if (!$exists[0]) {
|
596
|
2 |
|
$sql = "INSERT INTO {$this->accessList} (roles_name, resources_name, access_name, allowed, apps_id, created_at) VALUES (?, ?, ?, ?, ?, ?)";
|
597
|
2 |
|
$params = [$roleName, $resourceName, $accessName, $action, $this->getApp()->getId(), date('Y-m-d H:i:s')];
|
598
|
|
|
} else {
|
599
|
|
|
$sql = "UPDATE {$this->accessList} SET allowed = ? " .
|
600
|
|
|
'WHERE roles_name = ? AND resources_name = ? AND access_name = ? AND apps_id = ?';
|
601
|
|
|
$params = [$action, $roleName, $resourceName, $accessName, $this->getApp()->getId()];
|
602
|
|
|
}
|
603
|
2 |
|
$this->connection->execute($sql, $params);
|
604
|
|
|
|
605
|
|
|
/**
|
606
|
|
|
* Update the access '*' in access_list
|
607
|
|
|
*/
|
608
|
2 |
|
$sql = "SELECT COUNT(*) FROM {$this->accessList} " .
|
609
|
2 |
|
'WHERE roles_name = ? AND resources_name = ? AND access_name = ? and apps_id = ?';
|
610
|
2 |
|
$exists = $this->connection->fetchOne($sql, null, [$roleName, $resourceName, '*', $this->getApp()->getId()]);
|
611
|
2 |
|
if (!$exists[0]) {
|
612
|
1 |
|
$sql = "INSERT INTO {$this->accessList} (roles_name, resources_name, access_name, allowed, apps_id, created_at) VALUES (?, ?, ?, ?, ? , ?)";
|
613
|
1 |
|
$this->connection->execute($sql, [$roleName, $resourceName, '*', $this->_defaultAccess, $this->getApp()->getId(), date('Y-m-d H:i:s')]);
|
614
|
|
|
}
|
615
|
|
|
|
616
|
2 |
|
return true;
|
617
|
|
|
}
|
618
|
|
|
|
619
|
|
|
/**
|
620
|
|
|
* Inserts/Updates a permission in the access list
|
621
|
|
|
*
|
622
|
|
|
* @param string $roleName
|
623
|
|
|
* @param string $resourceName
|
624
|
|
|
* @param array|string $access
|
625
|
|
|
* @param integer $action
|
626
|
|
|
* @throws \Phalcon\Acl\Exception
|
627
|
|
|
*/
|
628
|
2 |
|
protected function allowOrDeny($roleName, $resourceName, $access, $action)
|
629
|
|
|
{
|
630
|
2 |
|
if (!$this->isRole($roleName)) {
|
631
|
|
|
throw new Exception("Role '{$roleName}' does not exist in the list");
|
632
|
|
|
}
|
633
|
2 |
|
if (!is_array($access)) {
|
634
|
|
|
$access = [$access];
|
635
|
|
|
}
|
636
|
2 |
|
foreach ($access as $accessName) {
|
637
|
2 |
|
$this->insertOrUpdateAccess($roleName, $resourceName, $accessName, $action);
|
638
|
|
|
}
|
639
|
|
|
|
640
|
2 |
|
return true;
|
641
|
|
|
}
|
642
|
|
|
}
|
643
|
|
|
|