Passed
Push — master ( 8f8ee7...6565b2 )
by Julito
11:41
created

ResourceNodeVoter::getEditorMask()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
nc 1
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
namespace Chamilo\CoreBundle\Security\Authorization\Voter;
6
7
use Chamilo\CoreBundle\Entity\Course;
8
use Chamilo\CoreBundle\Entity\ResourceLink;
9
use Chamilo\CoreBundle\Entity\ResourceNode;
10
use Chamilo\CoreBundle\Entity\ResourceRight;
11
use Chamilo\CoreBundle\Entity\Session;
12
use Chamilo\CourseBundle\Entity\CGroup;
13
use Laminas\Permissions\Acl\Acl;
14
use Laminas\Permissions\Acl\Resource\GenericResource as SecurityResource;
15
use Laminas\Permissions\Acl\Role\GenericRole as Role;
16
use Symfony\Component\HttpFoundation\RequestStack;
17
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
18
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
19
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
20
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
21
use Symfony\Component\Security\Core\Security;
22
use Symfony\Component\Security\Core\User\UserInterface;
23
24
/**
25
 * Class ResourceNodeVoter.
26
 */
27
class ResourceNodeVoter extends Voter
28
{
29
    public const VIEW = 'VIEW';
30
    public const CREATE = 'CREATE';
31
    public const EDIT = 'EDIT';
32
    public const DELETE = 'DELETE';
33
    public const EXPORT = 'EXPORT';
34
35
    public const ROLE_CURRENT_COURSE_TEACHER = 'ROLE_CURRENT_COURSE_TEACHER';
36
    public const ROLE_CURRENT_COURSE_STUDENT = 'ROLE_CURRENT_COURSE_STUDENT';
37
    public const ROLE_CURRENT_COURSE_GROUP_TEACHER = 'ROLE_CURRENT_COURSE_GROUP_TEACHER';
38
    public const ROLE_CURRENT_COURSE_GROUP_STUDENT = 'ROLE_CURRENT_COURSE_GROUP_STUDENT';
39
    public const ROLE_CURRENT_SESSION_COURSE_TEACHER = 'ROLE_CURRENT_SESSION_COURSE_TEACHER';
40
    public const ROLE_CURRENT_SESSION_COURSE_STUDENT = 'ROLE_CURRENT_SESSION_COURSE_STUDENT';
41
42
    private $requestStack;
43
    private $security;
44
45
    public function __construct(Security $security, RequestStack $requestStack)
46
    {
47
        $this->security = $security;
48
        $this->requestStack = $requestStack;
49
    }
50
51
    public static function getReaderMask(): int
52
    {
53
        $builder = new MaskBuilder();
54
        $builder
55
            ->add(self::VIEW)
56
        ;
57
58
        return $builder->get();
59
    }
60
61
    public static function getEditorMask(): int
62
    {
63
        $builder = new MaskBuilder();
64
        $builder
65
            ->add(self::VIEW)
66
            ->add(self::EDIT)
67
        ;
68
69
        return $builder->get();
70
    }
71
72
    protected function supports(string $attribute, $subject): bool
73
    {
74
        $options = [
75
            self::VIEW,
76
            self::CREATE,
77
            self::EDIT,
78
            self::DELETE,
79
            self::EXPORT,
80
        ];
81
82
        // if the attribute isn't one we support, return false
83
        if (!in_array($attribute, $options)) {
84
            return false;
85
        }
86
87
        // only vote on ResourceNode objects inside this voter
88
        if (!$subject instanceof ResourceNode) {
89
            return false;
90
        }
91
92
        return true;
93
    }
94
95
    protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
96
    {
97
        $user = $token->getUser();
98
99
        // Make sure there is a user object (i.e. that the user is logged in)
100
        // Update. No, anons can enter a node depending in the visibility.
101
        /*if (!$user instanceof UserInterface) {
102
            return false;
103
        }*/
104
105
        /** @var ResourceNode $resourceNode */
106
        $resourceNode = $subject;
107
        $resourceTypeName = $resourceNode->getResourceType()->getName();
108
109
        // Illustrations are always visible.
110
        if ('illustrations' === $resourceTypeName) {
111
            return true;
112
        }
113
114
        // Courses are also a Resource but courses are protected using the CourseVoter, not by ResourceNodeVoter.
115
        if ('courses' === $resourceTypeName) {
116
            return true;
117
        }
118
119
        // Checking admin role.
120
        if ($this->security->isGranted('ROLE_ADMIN')) {
121
            return true;
122
        }
123
124
        // @todo
125
        switch ($attribute) {
126
            case self::VIEW:
127
                break;
128
            case self::EDIT:
129
                break;
130
        }
131
132
        // Check if I'm the owner.
133
        $creator = $resourceNode->getCreator();
134
        if ($creator instanceof UserInterface &&
135
            $user instanceof UserInterface &&
136
            $user->getUsername() === $creator->getUsername()) {
137
            return true;
138
        }
139
140
        // Checking links connected to this resource.
141
        $request = $this->requestStack->getCurrentRequest();
142
143
        $courseId = (int) $request->get('cid');
144
        $sessionId = (int) $request->get('sid');
145
        $groupId = (int) $request->get('gid');
146
147
        $links = $resourceNode->getResourceLinks();
148
        $linkFound = false;
149
        //$courseManager = $this->entityManager->getRepository(Course::class);
150
        //$sessionManager = $this->entityManager->getRepository(Session::class);
151
152
        $course = null;
153
        $link = null;
154
155
        // @todo implement view, edit, delete.
156
        foreach ($links as $link) {
157
            // Block access if visibility is deleted. Creator and admin already can access before.
158
            if (ResourceLink::VISIBILITY_DELETED === $link->getVisibility()) {
159
                $linkFound = false;
160
161
                break;
162
            }
163
164
            // Check if resource was sent to the current user.
165
            $linkUser = $link->getUser();
166
            if ($linkUser instanceof UserInterface && $linkUser->getUsername() === $creator->getUsername()) {
167
                $linkFound = true;
168
169
                break;
170
            }
171
172
            $linkCourse = $link->getCourse();
173
174
            // Course found, but courseId not set, skip course checking.
175
            if ($linkCourse instanceof Course && empty($courseId)) {
176
                continue;
177
            }
178
179
            $linkSession = $link->getSession();
180
            $linkGroup = $link->getGroup();
181
            //$linkUserGroup = $link->getUserGroup();
182
183
            // @todo Check if resource was sent to a usergroup
184
185
            // Check if resource was sent inside a group in a course session.
186
            if ($linkGroup instanceof CGroup && !empty($groupId) &&
187
                $linkSession instanceof Session && !empty($sessionId) &&
188
                $linkCourse instanceof Course && !empty($courseId)
189
            ) {
190
                if ($linkCourse->getId() === $courseId &&
191
                    $linkSession->getId() === $sessionId &&
192
                    $linkGroup->getIid() === $groupId
193
                ) {
194
                    $linkFound = true;
195
196
                    break;
197
                }
198
            }
199
200
            // Check if resource was sent inside a group in a base course.
201
            if ($linkGroup instanceof CGroup && !empty($groupId) &&
202
                empty($sessionId) &&
203
                $linkCourse instanceof Course && !empty($courseId)
204
            ) {
205
                if ($linkCourse->getId() === $courseId &&
206
                    $linkGroup->getIid() === $groupId
207
                ) {
208
                    $linkFound = true;
209
210
                    break;
211
                }
212
            }
213
214
            // Check if resource was sent to a course inside a session.
215
            if ($linkSession instanceof Session && !empty($sessionId) &&
216
                $linkCourse instanceof Course && !empty($courseId)
217
            ) {
218
                if ($linkCourse->getId() === $courseId &&
219
                    $linkSession->getId() === $sessionId
220
                ) {
221
                    $linkFound = true;
222
223
                    break;
224
                }
225
            }
226
227
            // Check if resource was sent to a course.
228
            if ($linkCourse instanceof Course && !empty($courseId)) {
229
                if ($linkCourse->getId() === $courseId) {
230
                    $linkFound = true;
231
232
                    break;
233
                }
234
            }
235
236
            /*if (ResourceLink::VISIBILITY_PUBLISHED === $link->getVisibility()) {
237
                $linkFound = true;
238
239
                break;
240
            }*/
241
        }
242
243
        var_dump($link->getId());
0 ignored issues
show
Security Debugging Code introduced by
var_dump($link->getId()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
Bug introduced by
The method getId() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

243
        var_dump($link->/** @scrutinizer ignore-call */ getId());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
244
        // No link was found or not available.
245
        if (false === $linkFound) {
246
            return false;
247
        }
248
249
        // Getting rights from the link
250
        $rightFromResourceLink = $link->getResourceRight();
251
        $allowAnonsToSee = false;
252
        $rights = [];
253
        if ($rightFromResourceLink->count() > 0) {
254
            // Taken rights from the link
255
            $rights = $rightFromResourceLink;
256
        } else {
257
            // Taken the rights from the default tool
258
            //$rights = $link->getResourceNode()->getTool()->getToolResourceRight();
259
            //$rights = $link->getResourceNode()->getResourceType()->getTool()->getToolResourceRight();
260
261
            // By default the rights are:
262
            // Teachers: CRUD.
263
            // Students: Only read.
264
            // Anons: Only read.
265
            $readerMask = self::getReaderMask();
266
            $editorMask = self::getEditorMask();
267
268
            if ($courseId) {
269
                $resourceRight = new ResourceRight();
270
                $resourceRight
271
                    ->setMask($editorMask)
272
                    ->setRole(self::ROLE_CURRENT_COURSE_TEACHER);
273
                $rights[] = $resourceRight;
274
275
                $resourceRight = new ResourceRight();
276
                $resourceRight
277
                    ->setMask($readerMask)
278
                    ->setRole(self::ROLE_CURRENT_COURSE_STUDENT);
279
                $rights[] = $resourceRight;
280
281
                if (ResourceLink::VISIBILITY_PUBLISHED === $link->getVisibility() && $link->getCourse()->isPublic()) {
282
                    $allowAnonsToSee = true;
283
                    $resourceRight = new ResourceRight();
284
                    $resourceRight
285
                        ->setMask($readerMask)
286
                        ->setRole('IS_AUTHENTICATED_ANONYMOUSLY');
287
                    $rights[] = $resourceRight;
288
                }
289
            }
290
291
            if (!empty($groupId)) {
292
                $resourceRight = new ResourceRight();
293
                $resourceRight
294
                    ->setMask($editorMask)
295
                    ->setRole(self::ROLE_CURRENT_SESSION_COURSE_TEACHER)
296
                ;
297
                $rights[] = $resourceRight;
298
299
                $resourceRight = new ResourceRight();
300
                $resourceRight
301
                    ->setMask($readerMask)
302
                    ->setRole(self::ROLE_CURRENT_SESSION_COURSE_STUDENT)
303
                ;
304
                $rights[] = $resourceRight;
305
            }
306
307
            if (!empty($sessionId)) {
308
                $resourceRight = new ResourceRight();
309
                $resourceRight
310
                    ->setMask($editorMask)
311
                    ->setRole(self::ROLE_CURRENT_SESSION_COURSE_TEACHER)
312
                ;
313
                $rights[] = $resourceRight;
314
315
                $resourceRight = new ResourceRight();
316
                $resourceRight
317
                    ->setMask($readerMask)
318
                    ->setRole(self::ROLE_CURRENT_SESSION_COURSE_STUDENT)
319
                ;
320
                $rights[] = $resourceRight;
321
            }
322
323
            if (empty($rights) && ResourceLink::VISIBILITY_PUBLISHED === $link->getVisibility()) {
324
                // Give just read access
325
                $resourceRight = new ResourceRight();
326
                $resourceRight
327
                    ->setMask($readerMask)
328
                    ->setRole('ROLE_USER')
329
                ;
330
                $rights[] = $resourceRight;
331
            }
332
        }
333
334
        // Asked mask
335
        $mask = new MaskBuilder();
336
        $mask->add($attribute);
337
        $askedMask = $mask->get();
338
339
        // Setting Laminas simple ACL
340
        $acl = new Acl();
341
342
        // Creating roles
343
        // @todo move this in a service
344
        $anon = new Role('IS_AUTHENTICATED_ANONYMOUSLY');
345
        $userRole = new Role('ROLE_USER');
346
        $student = new Role('ROLE_STUDENT');
347
        $teacher = new Role('ROLE_TEACHER');
348
349
        $currentStudent = new Role(self::ROLE_CURRENT_COURSE_STUDENT);
350
        $currentTeacher = new Role(self::ROLE_CURRENT_COURSE_TEACHER);
351
352
        $currentStudentSession = new Role(self::ROLE_CURRENT_SESSION_COURSE_STUDENT);
353
        $currentTeacherSession = new Role(self::ROLE_CURRENT_SESSION_COURSE_TEACHER);
354
355
        $superAdmin = new Role('ROLE_SUPER_ADMIN');
356
        $admin = new Role('ROLE_ADMIN');
357
358
        // Adding roles to the ACL.
359
        $acl
360
            ->addRole($anon)
361
            ->addRole($userRole)
362
            ->addRole($student)
363
            ->addRole($teacher)
364
            ->addRole($currentStudent)
365
            ->addRole($currentTeacher, self::ROLE_CURRENT_COURSE_STUDENT)
366
            ->addRole($currentStudentSession)
367
            ->addRole($currentTeacherSession, self::ROLE_CURRENT_SESSION_COURSE_STUDENT)
368
            ->addRole($superAdmin)
369
            ->addRole($admin)
370
        ;
371
372
        // Add a security resource.
373
        $securityResource = new SecurityResource($link);
374
        $acl->addResource($securityResource);
375
376
        // Check all the right this link has.
377
        // Set rights from the ResourceRight.
378
        foreach ($rights as $right) {
379
            $acl->allow($right->getRole(), null, $right->getMask());
380
        }
381
382
        // var_dump($askedMask, $roles);
383
        // Role and permissions settings
384
        // Student can just view (read)
385
        $acl->allow($student, null, self::getReaderMask());
386
387
        // Anons can see.
388
        if ($allowAnonsToSee) {
389
            $acl->allow($anon, null, self::getReaderMask());
390
        }
391
392
        // Teacher can view/edit
393
        $acl->allow(
394
            $teacher,
395
            null,
396
            [
397
                self::getReaderMask(),
398
                self::getEditorMask(),
399
            ]
400
        );
401
402
        // Admin can do everything
403
        $acl->allow($admin);
404
        $acl->allow($superAdmin);
405
406
        if ($token instanceof AnonymousToken) {
407
            if ($acl->isAllowed('IS_AUTHENTICATED_ANONYMOUSLY', $securityResource, $askedMask)) {
408
                return true;
409
            }
410
411
            return false;
412
        }
413
414
        foreach ($user->getRoles() as $role) {
415
            if ($acl->isAllowed($role, $securityResource, $askedMask)) {
416
                return true;
417
            }
418
        }
419
420
        //dump('not allowed to '.$attribute);
421
        return false;
422
    }
423
}
424