1
|
|
|
<?php |
2
|
|
|
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Casbin; |
6
|
|
|
|
7
|
|
|
use Casbin\Effect\DefaultEffector; |
8
|
|
|
use Casbin\Effect\Effector; |
9
|
|
|
use Casbin\Exceptions\CasbinException; |
10
|
|
|
use Casbin\Exceptions\InvalidFilePathException; |
11
|
|
|
use Casbin\Model\FunctionMap; |
12
|
|
|
use Casbin\Model\Model; |
13
|
|
|
use Casbin\Persist\Adapter; |
14
|
|
|
use Casbin\Persist\Adapters\FileAdapter; |
15
|
|
|
use Casbin\Persist\FilteredAdapter; |
16
|
|
|
use Casbin\Persist\Watcher; |
17
|
|
|
use Casbin\Rbac\DefaultRoleManager\RoleManager as DefaultRoleManager; |
18
|
|
|
use Casbin\Rbac\RoleManager; |
19
|
|
|
use Casbin\Util\BuiltinOperations; |
20
|
|
|
use Casbin\Log\Log; |
21
|
|
|
use Casbin\Util\Util; |
22
|
|
|
use Symfony\Component\ExpressionLanguage\ExpressionLanguage; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Class Enforcer |
26
|
|
|
* the main interface for authorization enforcement and policy management. |
27
|
|
|
* |
28
|
|
|
* @author [email protected] |
|
|
|
|
29
|
|
|
*/ |
|
|
|
|
30
|
|
|
class Enforcer |
31
|
|
|
{ |
32
|
|
|
use InternalApi; |
33
|
|
|
use ManagementApi; |
34
|
|
|
use RbacApi; |
|
|
|
|
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* model path. |
|
|
|
|
38
|
|
|
* |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
protected $modelPath; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Model. |
45
|
|
|
* |
46
|
|
|
* @var Model |
47
|
|
|
*/ |
48
|
|
|
protected $model; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* FunctionMap. |
52
|
|
|
* |
53
|
|
|
* @var FunctionMap |
54
|
|
|
*/ |
55
|
|
|
protected $fm; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Effector. |
59
|
|
|
* |
60
|
|
|
* @var Effector |
61
|
|
|
*/ |
62
|
|
|
protected $eft; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Adapter. |
66
|
|
|
* |
67
|
|
|
* @var Adapter|null |
68
|
|
|
*/ |
69
|
|
|
protected $adapter; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Watcher. |
73
|
|
|
* |
74
|
|
|
* @var Watcher|null |
75
|
|
|
*/ |
76
|
|
|
protected $watcher; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* RoleManager. |
80
|
|
|
* |
81
|
|
|
* @var RoleManager |
82
|
|
|
*/ |
83
|
|
|
protected $rm; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* $enabled. |
87
|
|
|
* |
88
|
|
|
* @var bool |
89
|
|
|
*/ |
90
|
|
|
protected $enabled; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* $autoSave. |
94
|
|
|
* |
95
|
|
|
* @var bool |
96
|
|
|
*/ |
97
|
|
|
protected $autoSave; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* $autoBuildRoleLinks. |
101
|
|
|
* |
102
|
|
|
* @var bool |
103
|
|
|
*/ |
104
|
|
|
protected $autoBuildRoleLinks; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Enforcer constructor. |
108
|
|
|
* Creates an enforcer via file or DB. |
109
|
|
|
* File: |
110
|
|
|
* $e = new Enforcer("path/to/basic_model.conf", "path/to/basic_policy.csv") |
111
|
|
|
* MySQL DB: |
112
|
|
|
* $a = DatabaseAdapter::newAdapter([ |
113
|
|
|
* 'type' => 'mysql', // mysql,pgsql,sqlite,sqlsrv |
114
|
|
|
* 'hostname' => '127.0.0.1', |
115
|
|
|
* 'database' => 'test', |
116
|
|
|
* 'username' => 'root', |
117
|
|
|
* 'password' => '123456', |
118
|
|
|
* 'hostport' => '3306', |
119
|
|
|
* ]); |
120
|
|
|
* $e = new Enforcer("path/to/basic_model.conf", $a). |
121
|
|
|
* |
122
|
|
|
* @param mixed ...$params |
|
|
|
|
123
|
|
|
* |
124
|
|
|
* @throws CasbinException |
125
|
|
|
*/ |
126
|
177 |
|
public function __construct(...$params) |
127
|
|
|
{ |
128
|
177 |
|
$parsedParamLen = 0; |
129
|
177 |
|
$paramLen = \count($params); |
130
|
177 |
|
if ($paramLen >= 1) { |
131
|
177 |
|
if (\is_bool($enableLog = $params[$paramLen - 1])) { |
132
|
6 |
|
$this->enableLog($enableLog); |
133
|
6 |
|
++$parsedParamLen; |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
137
|
177 |
|
if (2 == $paramLen - $parsedParamLen) { |
138
|
156 |
|
$p0 = $params[0]; |
139
|
156 |
|
if (\is_string($p0)) { |
140
|
150 |
|
$p1 = $params[1]; |
141
|
150 |
|
if (\is_string($p1)) { |
142
|
147 |
|
$this->initWithFile($p0, $p1); |
143
|
|
|
} else { |
144
|
150 |
|
$this->initWithAdapter($p0, $p1); |
145
|
|
|
} |
146
|
|
|
} else { |
147
|
6 |
|
if (\is_string($params[1])) { |
148
|
|
|
throw new CasbinException('Invalid parameters for enforcer.'); |
149
|
|
|
} else { |
150
|
156 |
|
$this->initWithModelAndAdapter($p0, $params[1]); |
151
|
|
|
} |
152
|
|
|
} |
153
|
24 |
|
} elseif (1 == $paramLen - $parsedParamLen) { |
154
|
21 |
|
$p0 = $params[0]; |
155
|
21 |
|
if (\is_string($p0)) { |
156
|
12 |
|
$this->initWithFile($p0, ''); |
157
|
|
|
} else { |
158
|
21 |
|
$this->initWithModelAndAdapter($p0, null); |
159
|
|
|
} |
160
|
3 |
|
} elseif (0 == $paramLen - $parsedParamLen) { |
161
|
|
|
// pass |
162
|
|
|
} else { |
163
|
|
|
throw new CasbinException('Invalid parameters for enforcer.'); |
164
|
|
|
} |
165
|
177 |
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* initializes an enforcer with a model file and a policy file. |
|
|
|
|
169
|
|
|
* |
170
|
|
|
* @param string $modelPath |
|
|
|
|
171
|
|
|
* @param string $policyPath |
|
|
|
|
172
|
|
|
* |
173
|
|
|
* @throws CasbinException |
174
|
|
|
*/ |
|
|
|
|
175
|
159 |
|
public function initWithFile(string $modelPath, string $policyPath): void |
176
|
|
|
{ |
177
|
159 |
|
$adapter = new FileAdapter($policyPath); |
178
|
159 |
|
$this->initWithAdapter($modelPath, $adapter); |
179
|
159 |
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* initializes an enforcer with a database adapter. |
|
|
|
|
183
|
|
|
* |
184
|
|
|
* @param string $modelPath |
|
|
|
|
185
|
|
|
* @param Adapter $adapter |
|
|
|
|
186
|
|
|
* |
187
|
|
|
* @throws CasbinException |
188
|
|
|
*/ |
|
|
|
|
189
|
162 |
|
public function initWithAdapter(string $modelPath, Adapter $adapter): void |
190
|
|
|
{ |
191
|
162 |
|
$m = Model::newModelFromFile($modelPath); |
192
|
162 |
|
$this->initWithModelAndAdapter($m, $adapter); |
193
|
|
|
|
194
|
162 |
|
$this->modelPath = $modelPath; |
195
|
162 |
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* initWithModelAndAdapter initializes an enforcer with a model and a database adapter. |
|
|
|
|
199
|
|
|
* |
200
|
|
|
* @param Model $m |
|
|
|
|
201
|
|
|
* @param Adapter|null $adapter |
|
|
|
|
202
|
|
|
*/ |
|
|
|
|
203
|
174 |
|
public function initWithModelAndAdapter(Model $m, Adapter $adapter = null): void |
204
|
|
|
{ |
205
|
174 |
|
$this->adapter = $adapter; |
206
|
|
|
|
207
|
174 |
|
$this->model = $m; |
208
|
174 |
|
$this->model->printModel(); |
209
|
|
|
|
210
|
174 |
|
$this->fm = Model::loadFunctionMap(); |
211
|
|
|
|
212
|
174 |
|
$this->initialize(); |
213
|
|
|
|
214
|
|
|
// Do not initialize the full policy when using a filtered adapter |
215
|
174 |
|
$ok = $this->adapter instanceof FilteredAdapter ? $this->adapter->isFiltered() : false; |
216
|
|
|
|
217
|
174 |
|
if (!\is_null($this->adapter) && !$ok) { |
218
|
165 |
|
$this->loadPolicy(); |
219
|
|
|
} |
220
|
174 |
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* initializes an enforcer with a database adapter. |
|
|
|
|
224
|
|
|
*/ |
|
|
|
|
225
|
177 |
|
protected function initialize(): void |
226
|
|
|
{ |
227
|
177 |
|
$this->rm = new DefaultRoleManager(10); |
228
|
177 |
|
$this->eft = new DefaultEffector(); |
229
|
177 |
|
$this->watcher = null; |
230
|
|
|
|
231
|
177 |
|
$this->enabled = true; |
232
|
177 |
|
$this->autoSave = true; |
233
|
177 |
|
$this->autoBuildRoleLinks = true; |
234
|
177 |
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* reloads the model from the model CONF file. |
|
|
|
|
238
|
|
|
* Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy(). |
239
|
|
|
* |
240
|
|
|
* @throws CasbinException |
241
|
|
|
*/ |
|
|
|
|
242
|
3 |
|
public function loadModel(): void |
243
|
|
|
{ |
244
|
3 |
|
$this->model = Model::newModelFromFile($this->modelPath); |
245
|
3 |
|
$this->model->printModel(); |
246
|
3 |
|
$this->fm = Model::loadFunctionMap(); |
247
|
|
|
|
248
|
3 |
|
$this->initialize(); |
249
|
3 |
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* gets the current model. |
|
|
|
|
253
|
|
|
* |
254
|
|
|
* @return Model |
255
|
|
|
*/ |
256
|
9 |
|
public function getModel(): Model |
257
|
|
|
{ |
258
|
9 |
|
return $this->model; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* sets the current model. |
|
|
|
|
263
|
|
|
* |
264
|
|
|
* @param Model $model |
|
|
|
|
265
|
|
|
*/ |
|
|
|
|
266
|
6 |
|
public function setModel(Model $model): void |
267
|
|
|
{ |
268
|
6 |
|
$this->model = $model; |
269
|
6 |
|
$this->fm = $this->model->loadFunctionMap(); |
270
|
|
|
|
271
|
6 |
|
$this->initialize(); |
272
|
6 |
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* gets the current adapter. |
|
|
|
|
276
|
|
|
* |
277
|
|
|
* @return Adapter |
278
|
|
|
*/ |
279
|
6 |
|
public function getAdapter(): Adapter |
280
|
|
|
{ |
281
|
6 |
|
return $this->adapter; |
|
|
|
|
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* sets the current adapter. |
|
|
|
|
286
|
|
|
* |
287
|
|
|
* @param Adapter $adapter |
|
|
|
|
288
|
|
|
*/ |
|
|
|
|
289
|
9 |
|
public function setAdapter(Adapter $adapter): void |
290
|
|
|
{ |
291
|
9 |
|
$this->adapter = $adapter; |
292
|
9 |
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* sets the current watcher. |
|
|
|
|
296
|
|
|
* |
297
|
|
|
* @param Watcher $watcher |
|
|
|
|
298
|
|
|
*/ |
|
|
|
|
299
|
|
|
public function setWatcher(Watcher $watcher): void |
300
|
|
|
{ |
301
|
|
|
$this->watcher = $watcher; |
302
|
|
|
$this->watcher->setUpdateCallback(function () { |
|
|
|
|
303
|
|
|
$this->loadPolicy(); |
304
|
|
|
}); |
|
|
|
|
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* gets the current role manager. |
|
|
|
|
309
|
|
|
* |
310
|
|
|
* @return RoleManager |
311
|
|
|
*/ |
312
|
6 |
|
public function getRoleManager(): RoleManager |
313
|
|
|
{ |
314
|
6 |
|
return $this->rm; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* sets the current role manager. |
|
|
|
|
319
|
|
|
* |
320
|
|
|
* @param RoleManager $rm |
|
|
|
|
321
|
|
|
*/ |
|
|
|
|
322
|
|
|
public function setRoleManager(RoleManager $rm): void |
323
|
|
|
{ |
324
|
|
|
$this->rm = $rm; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
|
|
|
|
328
|
|
|
* @param string $name |
|
|
|
|
329
|
|
|
* @param \Closure $fn |
|
|
|
|
330
|
|
|
*/ |
|
|
|
|
331
|
6 |
|
public function addMatchingFunc(string $name, \Closure $fn): void |
332
|
|
|
{ |
333
|
6 |
|
$this->rm->addMatchingFunc($name, $fn); |
334
|
6 |
|
} |
335
|
|
|
|
336
|
|
|
/** |
|
|
|
|
337
|
|
|
* @param string $name |
|
|
|
|
338
|
|
|
* @param \Closure $fn |
|
|
|
|
339
|
|
|
*/ |
|
|
|
|
340
|
6 |
|
public function addDomainMatchingFunc(string $name, \Closure $fn): void |
341
|
|
|
{ |
342
|
6 |
|
$this->rm->addDomainMatchingFunc($name, $fn); |
|
|
|
|
343
|
6 |
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* sets the current effector. |
|
|
|
|
347
|
|
|
* |
348
|
|
|
* @param Effector $eft |
|
|
|
|
349
|
|
|
*/ |
|
|
|
|
350
|
|
|
public function setEffector(Effector $eft): void |
351
|
|
|
{ |
352
|
|
|
$this->eft = $eft; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* clears all policy. |
|
|
|
|
357
|
|
|
*/ |
|
|
|
|
358
|
3 |
|
public function clearPolicy(): void |
359
|
|
|
{ |
360
|
3 |
|
$this->model->clearPolicy(); |
361
|
3 |
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* reloads the policy from file/database. |
|
|
|
|
365
|
|
|
*/ |
|
|
|
|
366
|
171 |
|
public function loadPolicy(): void |
367
|
|
|
{ |
368
|
171 |
|
$this->model->clearPolicy(); |
369
|
|
|
|
370
|
|
|
try { |
371
|
171 |
|
$this->adapter->loadPolicy($this->model); |
|
|
|
|
372
|
12 |
|
} catch (InvalidFilePathException $e) { |
373
|
|
|
//throw $e; |
374
|
|
|
} |
375
|
|
|
|
376
|
171 |
|
$this->model->printPolicy(); |
377
|
171 |
|
if ($this->autoBuildRoleLinks) { |
378
|
171 |
|
$this->buildRoleLinks(); |
379
|
|
|
} |
380
|
171 |
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* reloads a filtered policy from file/database. |
|
|
|
|
384
|
|
|
* |
385
|
|
|
* @param mixed $filter |
|
|
|
|
386
|
|
|
* |
387
|
|
|
* @throws CasbinException |
388
|
|
|
*/ |
|
|
|
|
389
|
3 |
|
public function loadFilteredPolicy($filter): void |
390
|
|
|
{ |
391
|
3 |
|
$this->model->clearPolicy(); |
392
|
|
|
|
393
|
3 |
|
if ($this->adapter instanceof FilteredAdapter) { |
394
|
3 |
|
$filteredAdapter = $this->adapter; |
395
|
3 |
|
$filteredAdapter->loadFilteredPolicy($this->model, $filter); |
396
|
|
|
} else { |
397
|
|
|
throw new CasbinException('filtered policies are not supported by this adapter'); |
398
|
|
|
} |
399
|
|
|
|
400
|
3 |
|
$this->model->printPolicy(); |
401
|
3 |
|
if ($this->autoBuildRoleLinks) { |
402
|
3 |
|
$this->buildRoleLinks(); |
403
|
|
|
} |
404
|
3 |
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* returns true if the loaded policy has been filtered. |
|
|
|
|
408
|
|
|
* |
409
|
|
|
* @return bool |
410
|
|
|
*/ |
411
|
6 |
|
public function isFiltered(): bool |
412
|
|
|
{ |
413
|
6 |
|
if (!$this->adapter instanceof FilteredAdapter) { |
414
|
3 |
|
return false; |
415
|
|
|
} |
416
|
|
|
|
417
|
3 |
|
$filteredAdapter = $this->adapter; |
418
|
|
|
|
419
|
3 |
|
return $filteredAdapter->isFiltered(); |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* saves the current policy (usually after changed with Casbin API) back to file/database. |
|
|
|
|
424
|
|
|
* |
425
|
|
|
* @return mixed |
426
|
|
|
* |
427
|
|
|
* @throws CasbinException |
428
|
|
|
*/ |
429
|
6 |
|
public function savePolicy(): void |
430
|
|
|
{ |
431
|
6 |
|
if ($this->isFiltered()) { |
432
|
3 |
|
throw new CasbinException('cannot save a filtered policy'); |
433
|
|
|
} |
434
|
|
|
|
435
|
3 |
|
$this->adapter->savePolicy($this->model); |
436
|
|
|
|
437
|
3 |
|
if (null !== $this->watcher) { |
438
|
|
|
$this->watcher->update(); |
439
|
|
|
} |
440
|
3 |
|
} |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function. |
|
|
|
|
444
|
|
|
* |
445
|
|
|
* @param bool $enabled |
|
|
|
|
446
|
|
|
*/ |
|
|
|
|
447
|
3 |
|
public function enableEnforce(bool $enabled = true): void |
448
|
|
|
{ |
449
|
3 |
|
$this->enabled = $enabled; |
450
|
3 |
|
} |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* changes whether Casbin will log messages to the Logger. |
|
|
|
|
454
|
|
|
* |
455
|
|
|
* @param bool $enabled |
|
|
|
|
456
|
|
|
*/ |
|
|
|
|
457
|
6 |
|
public function enableLog(bool $enabled = true): void |
458
|
|
|
{ |
459
|
6 |
|
Log::getLogger()->enableLog($enabled); |
460
|
6 |
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* controls whether to save a policy rule automatically to the adapter when it is added or removed. |
|
|
|
|
464
|
|
|
* |
465
|
|
|
* @param bool $autoSave |
|
|
|
|
466
|
|
|
*/ |
|
|
|
|
467
|
3 |
|
public function enableAutoSave(bool $autoSave = true): void |
468
|
|
|
{ |
469
|
3 |
|
$this->autoSave = $autoSave; |
470
|
3 |
|
} |
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* controls whether to rebuild the role inheritance relations when a role is added or deleted. |
|
|
|
|
474
|
|
|
* |
475
|
|
|
* @param bool $autoBuildRoleLinks |
|
|
|
|
476
|
|
|
*/ |
|
|
|
|
477
|
|
|
public function enableAutoBuildRoleLinks(bool $autoBuildRoleLinks = true): void |
478
|
|
|
{ |
479
|
|
|
$this->autoBuildRoleLinks = $autoBuildRoleLinks; |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* manually rebuild the role inheritance relations. |
|
|
|
|
484
|
|
|
*/ |
|
|
|
|
485
|
171 |
|
public function buildRoleLinks(): void |
486
|
|
|
{ |
487
|
171 |
|
$this->rm->clear(); |
488
|
171 |
|
$this->model->buildRoleLinks($this->rm); |
489
|
171 |
|
} |
490
|
|
|
|
491
|
|
|
/** |
492
|
|
|
* use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", |
|
|
|
|
493
|
|
|
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "". |
494
|
|
|
* |
495
|
|
|
* @param string $matcher |
|
|
|
|
496
|
|
|
* @param mixed ...$rvals |
|
|
|
|
497
|
|
|
* |
498
|
|
|
* @return bool|mixed |
499
|
|
|
* |
500
|
|
|
* @throws CasbinException |
501
|
|
|
*/ |
502
|
120 |
|
protected function enforcing(string $matcher, ...$rvals): bool |
503
|
|
|
{ |
504
|
120 |
|
if (!$this->enabled) { |
505
|
3 |
|
return true; |
506
|
|
|
} |
507
|
|
|
|
508
|
120 |
|
$functions = $this->fm->getFunctions(); |
509
|
|
|
|
510
|
120 |
|
if (isset($this->model['g'])) { |
511
|
45 |
|
foreach ($this->model['g'] as $key => $ast) { |
512
|
45 |
|
$rm = $ast->rM; |
513
|
45 |
|
$functions[$key] = BuiltinOperations::generateGFunction($rm); |
514
|
|
|
} |
515
|
|
|
} |
516
|
|
|
|
517
|
120 |
|
if (!isset($this->model['m']['m'])) { |
518
|
|
|
throw new CasbinException('model is undefined'); |
519
|
|
|
} |
520
|
|
|
|
521
|
120 |
|
$expString = ''; |
522
|
120 |
|
if ('' === $matcher) { |
523
|
120 |
|
$expString = $this->getExpString($this->model['m']['m']->value); |
524
|
|
|
} else { |
525
|
|
|
$expString = Util::removeComments(Util::escapeAssertion($matcher)); |
526
|
|
|
} |
527
|
|
|
|
528
|
120 |
|
$rTokens = array_values($this->model['r']['r']->tokens); |
529
|
120 |
|
$pTokens = array_values($this->model['p']['p']->tokens); |
530
|
|
|
|
531
|
120 |
|
$rParameters = array_combine($rTokens, $rvals); |
532
|
|
|
|
533
|
120 |
|
if (false === $rParameters) { |
534
|
|
|
throw new CasbinException('invalid request size'); |
535
|
|
|
} |
536
|
|
|
|
537
|
120 |
|
$expressionLanguage = $this->getExpressionLanguage($functions); |
538
|
120 |
|
$expression = $expressionLanguage->parse($expString, array_merge($rTokens, $pTokens)); |
539
|
|
|
|
540
|
120 |
|
$policyEffects = []; |
541
|
120 |
|
$matcherResults = []; |
542
|
|
|
|
543
|
120 |
|
$policyLen = \count($this->model['p']['p']->policy); |
544
|
|
|
|
545
|
120 |
|
if (0 != $policyLen) { |
546
|
114 |
|
foreach ($this->model['p']['p']->policy as $i => $pvals) { |
547
|
114 |
|
$parameters = array_combine($pTokens, $pvals); |
548
|
114 |
|
if (false === $parameters) { |
549
|
|
|
throw new CasbinException('invalid policy size'); |
550
|
|
|
} |
551
|
|
|
|
552
|
114 |
|
$parameters = array_merge($rParameters, $parameters); |
553
|
114 |
|
$result = $expressionLanguage->evaluate($expression, $parameters); |
554
|
|
|
|
555
|
114 |
|
if (\is_bool($result)) { |
556
|
114 |
|
if (!$result) { |
557
|
108 |
|
$policyEffects[$i] = Effector::INDETERMINATE; |
558
|
|
|
|
559
|
114 |
|
continue; |
560
|
|
|
} |
561
|
|
|
} elseif (\is_float($result)) { |
562
|
|
|
if (0 == $result) { |
563
|
|
|
$policyEffects[$i] = Effector::INDETERMINATE; |
564
|
|
|
|
565
|
|
|
continue; |
566
|
|
|
} else { |
567
|
|
|
$matcherResults[$i] = $result; |
568
|
|
|
} |
569
|
|
|
} else { |
570
|
|
|
throw new CasbinException('matcher result should be bool, int or float'); |
571
|
|
|
} |
572
|
111 |
|
if (isset($parameters['p_eft'])) { |
573
|
12 |
|
$eft = $parameters['p_eft']; |
574
|
12 |
|
if ('allow' == $eft) { |
575
|
9 |
|
$policyEffects[$i] = Effector::ALLOW; |
576
|
12 |
|
} elseif ('deny' == $eft) { |
577
|
9 |
|
$policyEffects[$i] = Effector::DENY; |
578
|
|
|
} else { |
579
|
12 |
|
$policyEffects[$i] = Effector::INDETERMINATE; |
580
|
|
|
} |
581
|
|
|
} else { |
582
|
99 |
|
$policyEffects[$i] = Effector::ALLOW; |
583
|
|
|
} |
584
|
|
|
|
585
|
111 |
|
if (isset($this->model['e']['e']) && 'priority(p_eft) || deny' == $this->model['e']['e']->value) { |
586
|
78 |
|
break; |
587
|
|
|
} |
588
|
|
|
} |
589
|
|
|
} else { |
590
|
12 |
|
$parameters = $rParameters; |
591
|
12 |
|
foreach ($this->model['p']['p']->tokens as $token) { |
592
|
12 |
|
$parameters[$token] = ''; |
593
|
|
|
} |
594
|
|
|
|
595
|
12 |
|
$result = $expressionLanguage->evaluate($expression, $parameters); |
596
|
|
|
|
597
|
12 |
|
if ($result) { |
598
|
3 |
|
$policyEffects[0] = Effector::ALLOW; |
599
|
|
|
} else { |
600
|
12 |
|
$policyEffects[0] = Effector::INDETERMINATE; |
601
|
|
|
} |
602
|
|
|
} |
603
|
|
|
|
604
|
120 |
|
$result = $this->eft->mergeEffects($this->model['e']['e']->value, $policyEffects, $matcherResults); |
605
|
|
|
|
606
|
120 |
|
if (Log::getLogger()->isEnabled()) { |
607
|
24 |
|
$reqStr = 'Request: '; |
608
|
24 |
|
$reqStr .= implode(', ', array_values($rvals)); |
609
|
|
|
|
610
|
24 |
|
$reqStr .= sprintf(' ---> %s', (string) $result); |
611
|
24 |
|
Log::logPrint($reqStr); |
612
|
|
|
} |
613
|
|
|
|
614
|
120 |
|
return $result; |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
/** |
|
|
|
|
618
|
|
|
* @param array $functions |
|
|
|
|
619
|
|
|
* |
620
|
|
|
* @return ExpressionLanguage |
621
|
|
|
*/ |
622
|
120 |
|
protected function getExpressionLanguage(array $functions): ExpressionLanguage |
623
|
|
|
{ |
624
|
120 |
|
$expressionLanguage = new ExpressionLanguage(); |
625
|
120 |
|
foreach ($functions as $key => $func) { |
626
|
|
|
$expressionLanguage->register($key, function (...$args) use ($key) { |
|
|
|
|
627
|
|
|
return sprintf($key.'(%1$s)', implode(',', $args)); |
628
|
|
|
}, function ($arguments, ...$args) use ($func) { |
|
|
|
|
629
|
60 |
|
return $func(...$args); |
630
|
120 |
|
}); |
|
|
|
|
631
|
|
|
} |
632
|
|
|
|
633
|
120 |
|
return $expressionLanguage; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
|
|
|
|
637
|
|
|
* @param string $expString |
|
|
|
|
638
|
|
|
* |
639
|
|
|
* @return string |
640
|
|
|
*/ |
641
|
120 |
|
protected function getExpString(string $expString): string |
642
|
|
|
{ |
643
|
120 |
|
return preg_replace_callback( |
644
|
120 |
|
'/([\s\S]*in\s+)\(([\s\S]+)\)([\s\S]*)/', |
645
|
|
|
function ($m) { |
646
|
|
|
return $m[1].'['.$m[2].']'.$m[3]; |
647
|
120 |
|
}, |
648
|
80 |
|
$expString |
649
|
|
|
); |
650
|
|
|
} |
651
|
|
|
|
652
|
|
|
/** |
653
|
|
|
* decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). |
|
|
|
|
654
|
|
|
* |
655
|
|
|
* @param mixed ...$rvals |
|
|
|
|
656
|
|
|
* |
657
|
|
|
* @return bool |
658
|
|
|
* |
659
|
|
|
* @throws CasbinException |
660
|
|
|
*/ |
661
|
120 |
|
public function enforce(...$rvals): bool |
662
|
|
|
{ |
663
|
120 |
|
return $this->enforcing('', ...$rvals); |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
/** |
667
|
|
|
* use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", |
|
|
|
|
668
|
|
|
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "". |
669
|
|
|
* |
670
|
|
|
* @param string $matcher |
|
|
|
|
671
|
|
|
* @param mixed ...$rvals |
|
|
|
|
672
|
|
|
* |
673
|
|
|
* @return bool |
674
|
|
|
* |
675
|
|
|
* @throws CasbinException |
676
|
|
|
*/ |
677
|
|
|
public function enforceWithMatcher(string $matcher, ...$rvals): bool |
678
|
|
|
{ |
679
|
|
|
return $this->enforcing($matcher, ...$rvals); |
680
|
|
|
} |
681
|
|
|
} |
682
|
|
|
|