Issues (964)

src/Security/Voter/DocumentVoter.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace App\Security\Voter;
4
5
use App\Utils\ArrayUtils;
6
use LogicException;
7
use App\Entity\Document;
8
use App\Entity\GradeMembership;
9
use App\Entity\Student;
10
use App\Entity\User;
11
use App\Entity\UserType;
12
use App\Entity\UserTypeEntity;
13
use App\Repository\DocumentRepositoryInterface;
14
use App\Section\SectionResolverInterface;
15
use App\Utils\EnumArrayUtils;
0 ignored issues
show
The type App\Utils\EnumArrayUtils was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
18
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
19
20
class DocumentVoter extends Voter {
21
22
    public const New = 'new-document';
23
    public const Edit = 'edit';
24
    public const Remove = 'remove';
25
    public const View = 'view';
26 10
    public const ViewOthers = 'other-documents';
27 10
    public const Admin = 'admin-documents';
28 10
29 10
    public function __construct(private SectionResolverInterface $sectionResolver, private AccessDecisionManagerInterface $accessDecisionManager, private DocumentRepositoryInterface $documentRepository)
30
    {
31
    }
32
33
    /**
34 10
     * @inheritDoc
35
     */
36 10
    protected function supports($attribute, $subject): bool {
37 10
        $attributes = [
38 10
            self::Edit,
39
            self::Remove,
40
            self::View,
41 10
        ];
42 10
43
        return $attribute === self::New || $attribute === self::ViewOthers || $attribute === self::Admin ||
44
            ($subject instanceof Document && in_array($attribute, $attributes));
45
    }
46
47
    /**
48
     * @inheritDoc
49
     */
50
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
51
    {
52
        return match ($attribute) {
53
            self::New => $this->canCreateDocument($token),
54
            self::Edit => $this->canEditDocument($subject, $token),
55
            self::Remove => $this->canRemoveDocument($token),
56
            self::View => $this->canViewDocument($subject, $token),
57
            self::ViewOthers => $this->canViewOtherDocuments($token),
58
            self::Admin => $this->canViewAdminOverview($token),
59
            default => throw new LogicException('This code should not be reached.'),
60
        };
61
    }
62
63
    private function canCreateDocument(TokenInterface $token): bool {
64
        return $this->accessDecisionManager->decide($token, ['ROLE_DOCUMENTS_ADMIN']);
65
    }
66
67
    private function canEditDocument(Document $document, TokenInterface $token): bool {
68
        if($this->accessDecisionManager->decide($token, ['ROLE_DOCUMENTS_ADMIN'])) {
69
            return true;
70
        }
71
72
        /** @var User $user */
73
        $user = $token->getUser();
74
75
        foreach($document->getAuthors() as $author) {
76
            if($author->getId() === $user->getId()) {
77
                return true;
78
            }
79
        }
80
81
        return false;
82
    }
83
84
    private function canRemoveDocument(TokenInterface $token): bool {
85
        return $this->accessDecisionManager->decide($token, ['ROLE_DOCUMENTS_ADMIN']);
86
    }
87
88
    private function canViewDocument(Document $document, TokenInterface $token): bool {
89
        if ($this->accessDecisionManager->decide($token, ['ROLE_DOCUMENTS_ADMIN']) || $this->accessDecisionManager->decide($token, ['ROLE_KIOSK'])) {
90
            return true;
91
        }
92
93
        /** @var User $user */
94
        $user = $token->getUser();
95
        $section = $this->sectionResolver->getCurrentSection();
96
97
        if (ArrayUtils::inArray($user->getUserType(), [UserType::Student, UserType::Parent, UserType::Intern]) !== true) {
98
            // Non students/parents/intern can view any student documents
99
            return true;
100
        }
101
102
        if ($section === null) {
103
            return false;
104
        }
105
106
        /** @var UserTypeEntity $visibility */
107
        foreach ($document->getVisibilities() as $visibility) {
108
            if ($user->getUserType() === $visibility->getUserType()) {
109
                if ($visibility->getUserType() !== UserType::Intern) {
110
                    // Check grade memberships for students/parents
111
                    $studentIds = $user->getStudents()->map(fn(Student $student) => $student->getId())->toArray();
112
113
                    foreach ($document->getGrades() as $documentStudyGroup) {
114
                        /** @var GradeMembership $membership */
115
                        foreach ($documentStudyGroup->getMemberships() as $membership) {
116
                            if ($membership->getSection()->getId() === $section->getId()) {
117
                                $studentId = $membership->getStudent()->getId();
118
119
                                if (in_array($studentId, $studentIds)) {
120
                                    return true;
121
                                }
122
                            }
123
                        }
124
                    }
125
                } else {
126
                    // Interns can view documents for Intern
127
                    return true;
128
                }
129
            }
130
        }
131
132
        return false;
133
    }
134
135
    private function canViewOtherDocuments(TokenInterface $token): bool {
136
        /** @var User $user */
137
        $user = $token->getUser();
138
        return $user->isTeacher() || $this->accessDecisionManager->decide($token, ['ROLE_DOCUMENTS_ADMIN']);
139
    }
140
141
    private function canViewAdminOverview(TokenInterface $token): bool {
142
        if($this->accessDecisionManager->decide($token, ['ROLE_DOCUMENTS_ADMIN'])) {
143
            return true;
144
        }
145
146
        /** @var User $user */
147
        $user = $token->getUser();
148
149
        return count($this->documentRepository->findAllByAuthor($user)) > 0;
150
    }
151
}