Failed Conditions
Pull Request — master (#10)
by Adrien
04:59 queued 01:35
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 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ecodev\Felix\Acl;
6
7
use Doctrine\Common\Util\ClassUtils;
8
use Ecodev\Felix\Model\CurrentUser;
9
use Ecodev\Felix\Model\Model;
10
11
class Acl extends \Laminas\Permissions\Acl\Acl
12
{
13
    /**
14
     * The message explaining the last denial.
15
     */
16
    private ?string $message = null;
17
18
    /**
19
     * @var string[]
20
     */
21
    private array $reasons = [];
22
23 2
    protected function createModelResource(string $class): ModelResource
24
    {
25 2
        $resource = new ModelResource($class);
26 2
        $this->addResource($resource);
27
28 2
        return $resource;
29
    }
30
31
    /**
32
     * Return whether the current user is allowed to do something.
33
     *
34
     * This should be the main method to do all ACL checks.
35
     */
36 2
    public function isCurrentUserAllowed(Model $model, string $privilege): bool
37
    {
38 2
        $resource = new ModelResource($this->getClass($model), $model);
39 2
        $role = $this->getCurrentRole();
40 2
        $this->reasons = [];
41
42 2
        $isAllowed = $this->isAllowed($role, $resource, $privilege);
43
44 2
        $this->message = $this->buildMessage($resource, $privilege, $role, $isAllowed);
45
46 2
        return $isAllowed;
47
    }
48
49
    /**
50
     * Set the reason for rejection that will be shown to end-user.
51
     *
52
     * This method always return false for usage convenience and should be used by all assertions,
53
     * instead of only return false themselves.
54
     *
55
     * @return false
56
     */
57 2
    public function reject(string $reason): bool
58
    {
59 2
        $this->reasons[] = $reason;
60
61 2
        return false;
62
    }
63
64 2
    private function getClass(Model $resource): string
65
    {
66 2
        return ClassUtils::getRealClass(get_class($resource));
67
    }
68
69 2
    private function getCurrentRole(): string
70
    {
71 2
        $user = CurrentUser::get();
72 2
        if (!$user) {
73 1
            return 'anonymous';
74
        }
75
76 2
        return $user->getRole();
77
    }
78
79 2
    private function buildMessage(ModelResource $resource, ?string $privilege, string $role, bool $isAllowed): ?string
80
    {
81 2
        if ($isAllowed) {
82
            return null;
83
        }
84
85 2
        $resource = $resource->getName();
86
87 2
        $user = CurrentUser::get();
88 2
        $userName = $user ? 'User "' . $user->getLogin() . '"' : 'Non-logged user';
89 2
        $privilege ??= 'NULL';
90
91 2
        $message = "$userName with role $role is not allowed on resource \"$resource\" with privilege \"$privilege\"";
92
93 2
        $count = count($this->reasons);
94 2
        if ($count === 1) {
95 1
            $message .= ' because ' . $this->reasons[0];
96 2
        } elseif ($count) {
97 1
            $list = array_map(fn ($reason) => '- ' . $reason, $this->reasons);
98 1
            $message .= ' because:' . PHP_EOL . PHP_EOL . implode(PHP_EOL, $list);
99
        }
100
101 2
        return $message;
102
    }
103
104
    /**
105
     * Returns the message explaining the last denial, if any.
106
     */
107 2
    public function getLastDenialMessage(): ?string
108
    {
109 2
        return $this->message;
110
    }
111
}
112