Passed
Pull Request — master (#8)
by Adrien
16:37 queued 14:00
created

Acl::isCurrentUserAllowed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

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