Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
StoreVoter
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
5 / 5
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 supports
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 voteOnAttribute
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 canView
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 canEdit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace App\Security;
6
7use App\Entity\Store;
8use App\Entity\User;
9use LogicException;
10use Override;
11use Symfony\Bundle\SecurityBundle\Security;
12use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
13use Symfony\Component\Security\Core\Authorization\Voter\Vote;
14use Symfony\Component\Security\Core\Authorization\Voter\Voter;
15
16/**
17 * @extends Voter<string, string>
18 */
19class StoreVoter extends Voter
20{
21    final public const string VIEW = 'view';
22
23    final public const string EDIT = 'edit';
24
25    final public const string EXPORT = 'export';
26
27    public function __construct(
28        private readonly Security $security
29    ) {}
30
31    #[Override]
32    protected function supports(string $attribute, mixed $subject): bool
33    {
34        if (!in_array($attribute, [self::VIEW, self::EDIT, self::EXPORT])) {
35            return false;
36        }
37
38        return $subject instanceof Store;
39    }
40
41    #[Override]
42    protected function voteOnAttribute(
43        string $attribute,
44        mixed $subject,
45        TokenInterface $token,
46        ?Vote $vote = null
47    ): bool
48    {
49        if ($this->security->isGranted('ROLE_ADMIN')) {
50            return true;
51        }
52
53        $user = $token->getUser();
54
55        if (!$user instanceof User) {
56            // the user must be logged in; if not, deny access
57            return false;
58        }
59
60        /** @var Store $store */
61        $store = $subject;
62
63        return match ($attribute) {
64            self::VIEW, self::EXPORT => $this->canView($store, $user),
65            self::EDIT => $this->canEdit(),
66            default => throw new LogicException('This code should not be reached!'),
67        };
68    }
69
70    private function canView(Store $store, User $user): bool
71    {
72        if ($this->security->isGranted(User::ROLES['cashier'])) {
73            return true;
74        }
75
76        return $store->getUser() === $user;
77    }
78
79    private function canEdit(): bool
80    {
81        return $this->security->isGranted('ROLE_ADMIN');
82    }
83}