Passed
Push — master ( 7d7c79...e83d7b )
by Sam
08:47
created

Acl::buildMessage()   A

Complexity

Conditions 6
Paths 17

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 17
nop 4
dl 0
loc 20
ccs 11
cts 11
cp 1
crap 6
rs 9.2222
c 0
b 0
f 0
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