1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Application\Acl; |
6
|
|
|
|
7
|
|
|
use Application\Acl\Assertion\IsMyself; |
8
|
|
|
use Application\Model\AbstractModel; |
9
|
|
|
use Application\Model\Configuration; |
10
|
|
|
use Application\Model\Country; |
11
|
|
|
use Application\Model\Event; |
12
|
|
|
use Application\Model\File; |
13
|
|
|
use Application\Model\Image; |
14
|
|
|
use Application\Model\Message; |
15
|
|
|
use Application\Model\News; |
16
|
|
|
use Application\Model\Newsletter; |
17
|
|
|
use Application\Model\Order; |
18
|
|
|
use Application\Model\OrderLine; |
19
|
|
|
use Application\Model\Product; |
20
|
|
|
use Application\Model\ProductTag; |
21
|
|
|
use Application\Model\Session; |
22
|
|
|
use Application\Model\Subscription; |
23
|
|
|
use Application\Model\User; |
24
|
|
|
use Application\Model\UserTag; |
25
|
|
|
use Doctrine\Common\Util\ClassUtils; |
26
|
|
|
|
27
|
|
|
class Acl extends \Laminas\Permissions\Acl\Acl |
28
|
|
|
{ |
29
|
|
|
/** |
30
|
|
|
* The message explaining the last denial |
31
|
|
|
* |
32
|
|
|
* @var null|string |
33
|
|
|
*/ |
34
|
|
|
private $message; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var null|string |
38
|
|
|
*/ |
39
|
|
|
private $reason; |
40
|
|
|
|
41
|
15 |
|
public function __construct() |
42
|
|
|
{ |
43
|
|
|
// Each role is strictly "stronger" than the last one |
44
|
15 |
|
$this->addRole(User::ROLE_ANONYMOUS); |
45
|
15 |
|
$this->addRole(User::ROLE_MEMBER, User::ROLE_ANONYMOUS); |
46
|
15 |
|
$this->addRole(User::ROLE_FACILITATOR, User::ROLE_MEMBER); |
47
|
15 |
|
$this->addRole(User::ROLE_ADMINISTRATOR, User::ROLE_FACILITATOR); |
48
|
|
|
|
49
|
15 |
|
$product = new ModelResource(Product::class); |
50
|
15 |
|
$productTag = new ModelResource(ProductTag::class); |
51
|
15 |
|
$image = new ModelResource(Image::class); |
52
|
15 |
|
$user = new ModelResource(User::class); |
53
|
15 |
|
$userTag = new ModelResource(UserTag::class); |
54
|
15 |
|
$file = new ModelResource(File::class); |
55
|
15 |
|
$message = new ModelResource(Message::class); |
56
|
15 |
|
$order = new ModelResource(Order::class); |
57
|
15 |
|
$orderLine = new ModelResource(OrderLine::class); |
58
|
15 |
|
$configuration = new ModelResource(Configuration::class); |
59
|
15 |
|
$event = new ModelResource(Event::class); |
60
|
15 |
|
$news = new ModelResource(News::class); |
61
|
15 |
|
$newsletter = new ModelResource(Newsletter::class); |
62
|
15 |
|
$session = new ModelResource(Session::class); |
63
|
15 |
|
$subscription = new ModelResource(Subscription::class); |
64
|
15 |
|
$country = new ModelResource(Country::class); |
65
|
|
|
|
66
|
15 |
|
$this->addResource($product); |
67
|
15 |
|
$this->addResource($productTag); |
68
|
15 |
|
$this->addResource($image); |
69
|
15 |
|
$this->addResource($user); |
70
|
15 |
|
$this->addResource($userTag); |
71
|
15 |
|
$this->addResource($file); |
72
|
15 |
|
$this->addResource($message); |
73
|
15 |
|
$this->addResource($order); |
74
|
15 |
|
$this->addResource($orderLine); |
75
|
15 |
|
$this->addResource($configuration); |
76
|
15 |
|
$this->addResource($event); |
77
|
15 |
|
$this->addResource($news); |
78
|
15 |
|
$this->addResource($newsletter); |
79
|
15 |
|
$this->addResource($session); |
80
|
15 |
|
$this->addResource($subscription); |
81
|
15 |
|
$this->addResource($country); |
82
|
|
|
|
83
|
15 |
|
$this->allow(User::ROLE_ANONYMOUS, [$configuration, $event, $news, $session, $product, $subscription, $productTag, $image, $country], ['read']); |
84
|
|
|
|
85
|
15 |
|
$this->allow(User::ROLE_MEMBER, [$user, $userTag], ['read']); |
86
|
15 |
|
$this->allow(User::ROLE_MEMBER, [$user], ['update'], new IsMyself()); |
87
|
15 |
|
$this->allow(User::ROLE_MEMBER, [$file], ['read']); |
88
|
15 |
|
$this->allow(User::ROLE_MEMBER, [$message], ['read']); |
89
|
15 |
|
$this->allow(User::ROLE_MEMBER, [$order, $orderLine], ['read']); |
90
|
15 |
|
$this->allow(User::ROLE_MEMBER, [$order], ['create']); |
91
|
|
|
|
92
|
15 |
|
$this->allow(User::ROLE_FACILITATOR, [$file], ['read', 'update']); |
93
|
15 |
|
$this->allow(User::ROLE_FACILITATOR, [$user], ['create', 'update']); |
94
|
15 |
|
$this->allow(User::ROLE_FACILITATOR, [$userTag], ['create', 'update', 'delete']); |
95
|
|
|
|
96
|
15 |
|
$this->allow(User::ROLE_ADMINISTRATOR, [$file, $event, $news, $session, $subscription, $product, $productTag, $country, $image], ['create', 'update', 'delete']); |
97
|
15 |
|
$this->allow(User::ROLE_ADMINISTRATOR, [$orderLine], ['update']); |
98
|
15 |
|
$this->allow(User::ROLE_ADMINISTRATOR, [$newsletter], ['create', 'read', 'update', 'delete']); |
99
|
15 |
|
$this->allow(User::ROLE_ADMINISTRATOR, [$configuration], ['create']); |
100
|
15 |
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Return whether the current user is allowed to do something |
104
|
|
|
* |
105
|
|
|
* This should be the main method to do all ACL checks. |
106
|
|
|
* |
107
|
|
|
* @param AbstractModel $model |
108
|
|
|
* @param string $privilege |
109
|
|
|
* |
110
|
|
|
* @return bool |
111
|
|
|
*/ |
112
|
14 |
|
public function isCurrentUserAllowed(AbstractModel $model, string $privilege): bool |
113
|
|
|
{ |
114
|
14 |
|
$resource = new ModelResource($this->getClass($model), $model); |
115
|
14 |
|
$role = $this->getCurrentRole(); |
116
|
14 |
|
$this->reason = null; |
117
|
|
|
|
118
|
14 |
|
$isAllowed = $this->isAllowed($role, $resource, $privilege); |
119
|
|
|
|
120
|
14 |
|
$this->message = $this->buildMessage($resource, $privilege, $role, $isAllowed); |
121
|
|
|
|
122
|
14 |
|
return $isAllowed; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Set the reason for rejection that will be shown to end-user |
127
|
|
|
* |
128
|
|
|
* This method always return false for usage convenience and should be used by all assertions, |
129
|
|
|
* instead of only return false themselves. |
130
|
|
|
* |
131
|
|
|
* @param string $reason |
132
|
|
|
* |
133
|
|
|
* @return false |
134
|
|
|
*/ |
135
|
2 |
|
public function reject(string $reason): bool |
136
|
|
|
{ |
137
|
2 |
|
$this->reason = $reason; |
138
|
|
|
|
139
|
2 |
|
return false; |
140
|
|
|
} |
141
|
|
|
|
142
|
14 |
|
private function getClass(AbstractModel $resource): string |
143
|
|
|
{ |
144
|
14 |
|
return ClassUtils::getRealClass(get_class($resource)); |
145
|
|
|
} |
146
|
|
|
|
147
|
14 |
|
private function getCurrentRole(): string |
148
|
|
|
{ |
149
|
14 |
|
$user = User::getCurrent(); |
150
|
14 |
|
if (!$user) { |
151
|
4 |
|
return User::ROLE_ANONYMOUS; |
152
|
|
|
} |
153
|
|
|
|
154
|
12 |
|
return $user->getRole(); |
155
|
|
|
} |
156
|
|
|
|
157
|
14 |
|
private function buildMessage($resource, ?string $privilege, string $role, bool $isAllowed): ?string |
158
|
|
|
{ |
159
|
14 |
|
if ($isAllowed) { |
160
|
10 |
|
return null; |
161
|
|
|
} |
162
|
|
|
|
163
|
7 |
|
if ($resource instanceof ModelResource) { |
164
|
7 |
|
$resource = $resource->getName(); |
165
|
|
|
} |
166
|
|
|
|
167
|
7 |
|
$user = User::getCurrent() ? 'User "' . User::getCurrent()->getLogin() . '"' : 'Non-logged user'; |
168
|
7 |
|
$privilege = $privilege === null ? 'NULL' : $privilege; |
169
|
|
|
|
170
|
7 |
|
$message = "$user with role $role is not allowed on resource \"$resource\" with privilege \"$privilege\""; |
171
|
|
|
|
172
|
7 |
|
if ($this->reason) { |
173
|
2 |
|
$message .= ' because ' . $this->reason; |
174
|
|
|
} |
175
|
|
|
|
176
|
7 |
|
return $message; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Returns the message explaining the last denial, if any |
181
|
|
|
* |
182
|
|
|
* @return null|string |
183
|
|
|
*/ |
184
|
2 |
|
public function getLastDenialMessage(): ?string |
185
|
|
|
{ |
186
|
2 |
|
return $this->message; |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
|