1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpAbac; |
4
|
|
|
|
5
|
|
|
use PhpAbac\Manager\{ |
6
|
|
|
AttributeManager, |
7
|
|
|
CacheManager, |
8
|
|
|
ComparisonManager, |
9
|
|
|
PolicyRuleManager |
10
|
|
|
}; |
11
|
|
|
use PhpAbac\Model\PolicyRuleAttribute; |
12
|
|
|
|
13
|
|
|
final class Abac |
14
|
|
|
{ |
15
|
|
|
/** @var PolicyRuleManager **/ |
16
|
|
|
private $policyRuleManager; |
17
|
|
|
/** @var AttributeManager **/ |
18
|
|
|
private $attributeManager; |
19
|
|
|
/** @var CacheManager **/ |
20
|
|
|
private $cacheManager; |
21
|
|
|
/** @var ComparisonManager **/ |
22
|
|
|
private $comparisonManager; |
23
|
|
|
/** @var array **/ |
24
|
|
|
private $errors; |
25
|
|
|
|
26
|
4 |
|
public function __construct(PolicyRuleManager $policyRuleManager, AttributeManager $attributeManager, ComparisonManager $comparisonManager, CacheManager $cacheManager) |
27
|
|
|
{ |
28
|
4 |
|
$this->attributeManager = $attributeManager; |
|
|
|
|
29
|
4 |
|
$this->policyRuleManager = $policyRuleManager; |
|
|
|
|
30
|
4 |
|
$this->cacheManager = $cacheManager; |
|
|
|
|
31
|
4 |
|
$this->comparisonManager = $comparisonManager; |
|
|
|
|
32
|
4 |
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Return true if both user and object respects all the rules conditions |
36
|
|
|
* If the objectId is null, policy rules about its attributes will be ignored |
37
|
|
|
* In case of mismatch between attributes and expected values, |
38
|
|
|
* an array with the concerned attributes slugs will be returned. |
39
|
|
|
* |
40
|
|
|
* Available options are : |
41
|
|
|
* * dynamic_attributes: array |
42
|
|
|
* * cache_result: boolean |
43
|
|
|
* * cache_ttl: integer |
44
|
|
|
* * cache_driver: string |
45
|
|
|
* |
46
|
|
|
* Available cache drivers are : |
47
|
|
|
* * memory |
48
|
|
|
*/ |
49
|
4 |
|
public function enforce(string $ruleName, $user, $resource = null, array $options = []): bool |
50
|
|
|
{ |
51
|
4 |
|
$this->errors = []; |
52
|
|
|
// If there is dynamic attributes, we pass them to the comparison manager |
53
|
|
|
// When a comparison will be performed, the passed values will be retrieved and used |
54
|
4 |
|
if (isset($options[ 'dynamic_attributes' ])) { |
55
|
1 |
|
$this->comparisonManager->setDynamicAttributes($options[ 'dynamic_attributes' ]); |
56
|
|
|
} |
57
|
|
|
// Retrieve cache value for the current rule and values if cache item is valid |
58
|
4 |
|
if (($cacheResult = isset($options[ 'cache_result' ]) && $options[ 'cache_result' ] === true) === true) { |
|
|
|
|
59
|
|
|
$cacheItem = $this->cacheManager->getItem("$ruleName-{$user->getId()}-" . (($resource !== null) ? $resource->getId() : ''), (isset($options[ 'cache_driver' ])) ? $options[ 'cache_driver' ] : null, (isset($options[ 'cache_ttl' ])) ? $options[ 'cache_ttl' ] : null); |
60
|
|
|
// We check if the cache value s valid before returning it |
61
|
|
|
if (($cacheValue = $cacheItem->get()) !== null) { |
62
|
|
|
return $cacheValue; |
63
|
|
|
} |
64
|
|
|
} |
65
|
4 |
|
$policyRules = $this->policyRuleManager->getRule($ruleName, $user, $resource); |
66
|
|
|
|
67
|
4 |
|
foreach ($policyRules as $policyRule) { |
68
|
|
|
// For each policy rule attribute, we retrieve the attribute value and proceed configured extra data |
69
|
4 |
|
foreach ($policyRule->getPolicyRuleAttributes() as $pra) { |
70
|
|
|
/** @var PolicyRuleAttribute $pra */ |
71
|
4 |
|
$attribute = $pra->getAttribute(); |
72
|
|
|
|
73
|
4 |
|
$getter_params = $this->prepareGetterParams($pra->getGetterParams(), $user, $resource); |
74
|
4 |
|
$attribute->setValue($this->attributeManager->retrieveAttribute($attribute, $user, $resource, $getter_params)); |
75
|
4 |
|
if (count($pra->getExtraData()) > 0) { |
76
|
3 |
|
$this->processExtraData($pra, $user, $resource); |
77
|
|
|
} |
78
|
4 |
|
$this->comparisonManager->compare($pra); |
79
|
|
|
} |
80
|
|
|
// The given result could be an array of rejected attributes or true |
81
|
|
|
// True means that the rule is correctly enforced for the given user and resource |
82
|
4 |
|
$this->errors = $this->comparisonManager->getResult(); |
83
|
4 |
|
if (count($this->errors) === 0) { |
84
|
4 |
|
break; |
85
|
|
|
} |
86
|
|
|
} |
87
|
4 |
|
if ($cacheResult) { |
88
|
|
|
$cacheItem->set((count($this->errors) > 0) ? $this->errors : true); |
|
|
|
|
89
|
|
|
$this->cacheManager->save($cacheItem); |
90
|
|
|
} |
91
|
4 |
|
return count($this->errors) === 0; |
92
|
|
|
} |
93
|
|
|
|
94
|
4 |
|
public function getErrors(): array |
95
|
|
|
{ |
96
|
4 |
|
return $this->errors; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Function to prepare Getter Params when getter require parameters ( this parameters must be specified in configuration file) |
101
|
|
|
* |
102
|
|
|
* @param $getter_params |
103
|
|
|
* @param $user |
104
|
|
|
* @param $resource |
105
|
|
|
* |
106
|
|
|
* @return array |
107
|
|
|
*/ |
108
|
4 |
|
private function prepareGetterParams($getter_params, $user, $resource) |
109
|
|
|
{ |
110
|
4 |
|
if (empty($getter_params)) { |
111
|
4 |
|
return []; |
112
|
|
|
} |
113
|
2 |
|
$values = []; |
114
|
2 |
|
foreach ($getter_params as $getter_name=>$params) { |
|
|
|
|
115
|
2 |
|
foreach ($params as $param) { |
116
|
2 |
|
if ('@' !== $param[ 'param_name' ][ 0 ]) { |
117
|
|
|
$values[$getter_name][] = $param[ 'param_value' ]; |
118
|
|
|
} else { |
119
|
2 |
|
$values[$getter_name][] = $this->attributeManager->retrieveAttribute($this->attributeManager->getAttribute($param[ 'param_value' ]), $user, $resource); |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
} |
123
|
2 |
|
return $values; |
124
|
|
|
} |
125
|
|
|
|
126
|
3 |
|
private function processExtraData(PolicyRuleAttribute $pra, $user, $resource) |
127
|
|
|
{ |
128
|
3 |
|
foreach ($pra->getExtraData() as $key => $data) { |
129
|
|
|
switch ($key) { |
130
|
3 |
|
case 'with': |
131
|
|
|
// This data has to be removed for it will be stored elsewhere |
132
|
|
|
// in the policy rule attribute |
133
|
3 |
|
$pra->removeExtraData('with'); |
134
|
|
|
// The "with" extra data is an array of attributes, which are objects |
135
|
|
|
// Once we process it as policy rule attributes, we set it as the main policy rule attribute value |
136
|
3 |
|
$subPolicyRuleAttributes = []; |
137
|
|
|
|
138
|
3 |
|
foreach ($this->policyRuleManager->processRuleAttributes($data, $user, $resource) as $subPolicyRuleAttribute) { |
139
|
3 |
|
$subPolicyRuleAttributes[] = $subPolicyRuleAttribute; |
140
|
|
|
} |
141
|
3 |
|
$pra->setValue($subPolicyRuleAttributes); |
142
|
|
|
// This data can be used in complex comparisons |
143
|
3 |
|
$pra->addExtraData('attribute', $pra->getAttribute()); |
144
|
3 |
|
$pra->addExtraData('user', $user); |
145
|
3 |
|
$pra->addExtraData('resource', $resource); |
146
|
3 |
|
break; |
147
|
|
|
} |
148
|
|
|
} |
149
|
3 |
|
} |
150
|
|
|
} |
151
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..