Failed Conditions
Push — master ( e9ca52...479d22 )
by Adrien
10:20
created

Acl::getLastDenialMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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\All;
8
use Application\Acl\Assertion\BookableAvailable;
9
use Application\Acl\Assertion\ExpenseClaimStatusIsNew;
10
use Application\Acl\Assertion\IsMyself;
11
use Application\Acl\Assertion\IsOwner;
12
use Application\Acl\Assertion\StatusIsNew;
13
use Application\Model\AbstractModel;
14
use Application\Model\Account;
15
use Application\Model\AccountingDocument;
16
use Application\Model\Bookable;
17
use Application\Model\BookableMetadata;
18
use Application\Model\BookableTag;
19
use Application\Model\Booking;
20
use Application\Model\Country;
21
use Application\Model\ExpenseClaim;
22
use Application\Model\Image;
23
use Application\Model\License;
24
use Application\Model\Message;
25
use Application\Model\Transaction;
26
use Application\Model\TransactionTag;
27
use Application\Model\User;
28
use Application\Model\UserTag;
29
use Doctrine\Common\Util\ClassUtils;
30
31
class Acl extends \Zend\Permissions\Acl\Acl
32
{
33
    /**
34
     * The message explaining the last denial
35
     *
36
     * @var null|string
37
     */
38
    private $message;
39
40
    /**
41
     * @var null|string
42
     */
43
    private $reason;
44
45 26
    public function __construct()
46
    {
47
        // Each role is strictly "stronger" than the last one
48 26
        $this->addRole(User::ROLE_ANONYMOUS);
49 26
        $this->addRole(User::ROLE_BOOKING_ONLY, User::ROLE_ANONYMOUS);
50 26
        $this->addRole(User::ROLE_INDIVIDUAL, User::ROLE_BOOKING_ONLY);
51 26
        $this->addRole(User::ROLE_MEMBER, User::ROLE_INDIVIDUAL);
52 26
        $this->addRole(User::ROLE_RESPONSIBLE, User::ROLE_MEMBER);
53 26
        $this->addRole(User::ROLE_ADMINISTRATOR, User::ROLE_RESPONSIBLE);
54
55 26
        $bookable = new ModelResource(Bookable::class);
56 26
        $bookableMetadata = new ModelResource(BookableMetadata::class);
57 26
        $bookableTag = new ModelResource(BookableTag::class);
58 26
        $booking = new ModelResource(Booking::class);
59 26
        $image = new ModelResource(Image::class);
60 26
        $license = new ModelResource(License::class);
61 26
        $user = new ModelResource(User::class);
62 26
        $userTag = new ModelResource(UserTag::class);
63 26
        $country = new ModelResource(Country::class);
64 26
        $account = new ModelResource(Account::class);
65 26
        $accountingDocument = new ModelResource(AccountingDocument::class);
66 26
        $transactionTag = new ModelResource(TransactionTag::class);
67 26
        $expenseClaim = new ModelResource(ExpenseClaim::class);
68 26
        $message = new ModelResource(Message::class);
69 26
        $transaction = new ModelResource(Transaction::class);
70
71 26
        $this->addResource($bookable);
72 26
        $this->addResource($bookableMetadata);
73 26
        $this->addResource($bookableTag);
74 26
        $this->addResource($booking);
75 26
        $this->addResource($image);
76 26
        $this->addResource($license);
77 26
        $this->addResource($user);
78 26
        $this->addResource($userTag);
79 26
        $this->addResource($country);
80 26
        $this->addResource($account);
81 26
        $this->addResource($accountingDocument);
82 26
        $this->addResource($transactionTag);
83 26
        $this->addResource($expenseClaim);
84 26
        $this->addResource($message);
85 26
        $this->addResource($transaction);
86
87 26
        $this->allow(User::ROLE_ANONYMOUS, [$country, $bookable, $bookableMetadata, $bookableTag, $image, $license, $transactionTag], ['read']);
88
89 26
        $this->allow(User::ROLE_BOOKING_ONLY, $booking, ['create'], new BookableAvailable());
90 26
        $this->allow(User::ROLE_BOOKING_ONLY, $booking, ['read', 'update']);
91
92 26
        $this->allow(User::ROLE_INDIVIDUAL, $user, ['read']);
93 26
        $this->allow(User::ROLE_INDIVIDUAL, $user, ['update'], new IsMyself());
94 26
        $this->allow(User::ROLE_INDIVIDUAL, [$expenseClaim], ['create']);
95 26
        $this->allow(User::ROLE_INDIVIDUAL, [$expenseClaim], ['read']);
96 26
        $this->allow(User::ROLE_INDIVIDUAL, [$expenseClaim], ['update', 'delete'], new All(new IsOwner(), new StatusIsNew()));
97 26
        $this->allow(User::ROLE_INDIVIDUAL, [$accountingDocument], ['create'], new ExpenseClaimStatusIsNew());
98 26
        $this->allow(User::ROLE_INDIVIDUAL, [$accountingDocument], ['read']);
99 26
        $this->allow(User::ROLE_INDIVIDUAL, [$accountingDocument], ['update', 'delete'], new All(new IsOwner(), new ExpenseClaimStatusIsNew()));
100 26
        $this->allow(User::ROLE_INDIVIDUAL, [$account], ['read']);
101 26
        $this->allow(User::ROLE_INDIVIDUAL, [$account], ['update'], new IsOwner());
102 26
        $this->allow(User::ROLE_INDIVIDUAL, $message, ['read']);
103
104 26
        $this->allow(User::ROLE_MEMBER, $user, ['create']);
105 26
        $this->allow(User::ROLE_MEMBER, $user, ['update'], new IsOwner());
106
107 26
        $this->allow(User::ROLE_RESPONSIBLE, [$transaction, $account, $transactionTag], ['read']);
108 26
        $this->allow(User::ROLE_RESPONSIBLE, [$expenseClaim, $accountingDocument], ['read', 'update']);
109 26
        $this->allow(User::ROLE_RESPONSIBLE, [$user], ['update']);
110 26
        $this->allow(User::ROLE_RESPONSIBLE, [$userTag], ['create', 'read', 'update', 'delete']);
111 26
        $this->allow(User::ROLE_RESPONSIBLE, [$bookable, $bookableMetadata, $bookableTag, $image, $license], ['create', 'update', 'delete']);
112 26
        $this->allow(User::ROLE_RESPONSIBLE, [$booking], ['delete']);
113
114 26
        $this->allow(User::ROLE_ADMINISTRATOR, [$transaction, $account, $transactionTag, $accountingDocument], ['create', 'update', 'delete']);
115 26
    }
116
117
    /**
118
     * Return whether the current user is allowed to do something
119
     *
120
     * This should be the main method to do all ACL checks.
121
     *
122
     * @param AbstractModel $model
123
     * @param string $privilege
124
     *
125
     * @return bool
126
     */
127 25
    public function isCurrentUserAllowed(AbstractModel $model, string $privilege): bool
128
    {
129 25
        $resource = new ModelResource($this->getClass($model), $model);
130 25
        $role = $this->getCurrentRole();
131 25
        $this->reason = null;
132
133 25
        $isAllowed = $this->isAllowed($role, $resource, $privilege);
134
135 25
        $this->message = $this->buildMessage($resource, $privilege, $role, $isAllowed);
136
137 25
        return $isAllowed;
138
    }
139
140
    /**
141
     * Set the reason for rejection that will be shown to end-user
142
     *
143
     * This method always return false for usage convenience and should be used by all assertions,
144
     * instead of only return false themselves.
145
     *
146
     * @param string $reason
147
     *
148
     * @return false
149
     */
150 5
    public function reject(string $reason): bool
151
    {
152 5
        $this->reason = $reason;
153
154 5
        return false;
155
    }
156
157 25
    private function getClass(AbstractModel $resource): string
158
    {
159 25
        return ClassUtils::getRealClass(get_class($resource));
160
    }
161
162 25
    private function getCurrentRole(): string
163
    {
164 25
        $user = User::getCurrent();
165 25
        if (!$user) {
166 5
            return User::ROLE_ANONYMOUS;
167
        }
168
169 24
        return $user->getRole();
170
    }
171
172 25
    private function buildMessage($resource, ?string $privilege, string $role, bool $isAllowed): ?string
173
    {
174 25
        if ($isAllowed) {
175 21
            return null;
176
        }
177
178 10
        if ($resource instanceof ModelResource) {
179 10
            $resource = $resource->getName();
180
        }
181
182 10
        $user = User::getCurrent() ? 'User "' . User::getCurrent()->getLogin() . '"' : 'Non-logged user';
183 10
        $privilege = $privilege === null ? 'NULL' : $privilege;
184
185 10
        $message = "$user with role $role is not allowed on resource \"$resource\" with privilege \"$privilege\"";
186
187 10
        if ($this->reason) {
188 5
            $message .= ' because ' . $this->reason;
189
        }
190
191 10
        return $message;
192
    }
193
194
    /**
195
     * Returns the message explaining the last denial, if any
196
     *
197
     * @return null|string
198
     */
199 4
    public function getLastDenialMessage(): ?string
200
    {
201 4
        return $this->message;
202
    }
203
}
204