DocumentVoter::canPostpone()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 11
rs 10
1
<?php
2
namespace EWW\Dpf\Security;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use EWW\Dpf\Domain\Model\Document;
18
use EWW\Dpf\Domain\Workflow\DocumentWorkflow;
19
use TYPO3\CMS\Extbase\Object\ObjectManager;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use EWW\Dpf\Domain\Repository\DocumentRepository;
22
use TYPO3\CMS\Core\Log\LogManager;
23
24
25
class DocumentVoter extends Voter
26
{
27
    const CREATE = "DOCUMENT_CREATE";
28
    const CREATE_REGISTER = "DOCUMENT_CREATE_REGISTER";
29
    const UPDATE = "DOCUMENT_UPDATE";
30
    const LIST = "DOCUMENT_LIST";
31
    const LIST_REGISTERED = "DOCUMENT_LIST_REGISTERED";
32
    const LIST_IN_PROGRESS = "DOCUMENT_LIST_IN_PROGRESS";
33
34
    const DISCARD = "DOCUMENT_DISCARD";
35
36
    const DELETE_LOCALLY = "DOCUMENT_DELETE_LOCALLY";
37
    const DELETE_WORKING_COPY = "DOCUMENT_DELETE_WORKING_COPY";
38
    const DUPLICATE = "DOCUMENT_DUPLICATE";
39
    const RELEASE_PUBLISH = "DOCUMENT_RELEASE_PUBLISH";
40
    const RELEASE_ACTIVATE = "DOCUMENT_RELEASE_ACTIVATE";
41
    const REGISTER = "DOCUMENT_REGISTER";
42
    const SHOW_DETAILS = "DOCUMENT_SHOW_DETAILS";
43
    const CANCEL_LIST_TASK = "DOCUMENT_CANCEL_LIST_TASK";
44
    const UPLOAD_FILES = "DOCUMENT_UPLOAD_FILES";
45
    const EDIT = "DOCUMENT_EDIT";
46
    const POSTPONE = "DOCUMENT_POSTPONE";
47
    const DOUBLET_CHECK = "DOCUMENT_DOUBLET_CHECK";
48
    const SUGGEST_RESTORE = "DOCUMENT_SUGGEST_RESTORE";
49
    const SUGGEST_MODIFICATION = "DOCUMENT_SUGGEST_MODIFICATION";
50
    const SUGGESTION_ACCEPT = "DOCUMENT_SUGGESTION_ACCEPT";
51
52
    /**
53
     * editingLockService
54
     *
55
     * @var \EWW\Dpf\Services\Document\EditingLockService
56
     * @TYPO3\CMS\Extbase\Annotation\Inject
57
     */
58
    protected $editingLockService = null;
59
60
    /**
61
     * workflow
62
     *
63
     * @var DocumentWorkflow
64
     */
65
    protected $workflow;
66
67
    public function __construct()
68
    {
69
       $this->workflow = DocumentWorkflow::getWorkflow();
0 ignored issues
show
Documentation Bug introduced by
It seems like EWW\Dpf\Domain\Workflow\...Workflow::getWorkflow() of type Symfony\Component\Workflow\Workflow is incompatible with the declared type EWW\Dpf\Domain\Workflow\DocumentWorkflow of property $workflow.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
70
    }
71
72
73
    /**
74
     * Returns all supported attributes.
75
     *
76
     * @return array
77
     */
78
    protected static function getAttributes()
79
    {
80
        return array(
81
            self::CREATE,
82
            self::CREATE_REGISTER,
83
            self::UPDATE,
84
            self::LIST,
85
            self::LIST_REGISTERED,
86
            self::LIST_IN_PROGRESS,
87
            self::DISCARD,
88
            self::DELETE_LOCALLY,
89
            self::DELETE_WORKING_COPY,
90
            self::DUPLICATE,
91
            self::RELEASE_PUBLISH,
92
            self::RELEASE_ACTIVATE,
93
            self::REGISTER,
94
            self::SHOW_DETAILS,
95
            self::CANCEL_LIST_TASK,
96
            self::UPLOAD_FILES,
97
            self::EDIT,
98
            self::POSTPONE,
99
            self::DOUBLET_CHECK,
100
            self::SUGGEST_RESTORE,
101
            self::SUGGEST_MODIFICATION,
102
            self::SUGGESTION_ACCEPT
103
        );
104
    }
105
106
107
    /**
108
     * Determines if the voter supports the given attribute.
109
     *
110
     * @param string $attribute
111
     * @param mixed $subject
112
     * @return mixed
113
     */
114
    public static function supports($attribute, $subject = NULL)
115
    {
116
        if (!in_array($attribute, self::getAttributes())) {
117
            return FALSE;
118
        }
119
120
        if (!$subject instanceof Document && !is_null($subject)) {
121
            return FALSE;
122
        }
123
124
        return TRUE;
125
    }
126
127
    /**
128
     * Determines if access for the given attribute and subject is allowed.
129
     *
130
     * @param string $attribute
131
     * @param mixed $subject
132
     * @return mixed
133
     */
134
    public function voteOnAttribute($attribute, $subject = NULL)
135
    {
136
        if (!$subject instanceof Document) {
137
            return FALSE;
138
        }
139
140
        switch ($attribute) {
141
142
            case self::CREATE:
143
                return $this->defaultAccess($subject);
0 ignored issues
show
Unused Code introduced by
The call to EWW\Dpf\Security\DocumentVoter::defaultAccess() has too many arguments starting with $subject. ( Ignorable by Annotation )

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

143
                return $this->/** @scrutinizer ignore-call */ defaultAccess($subject);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
144
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
145
146
            case self::CREATE_REGISTER:
147
                return $this->canCreateRegister($subject);
148
                break;
149
150
            case self::UPDATE:
151
                return $this->canUpdate($subject);
152
                break;
153
154
            case self::LIST:
155
                return $this->defaultAccess();
156
                break;
157
158
            case self::LIST_REGISTERED:
159
                return $this->defaultAccess();
160
                break;
161
162
            case self::LIST_IN_PROGRESS:
163
                return $this->defaultAccess();
164
                break;
165
166
            case self::DISCARD:
167
                return $this->canDiscard($subject);
168
                break;
169
170
            case self::DELETE_LOCALLY:
171
                return $this->canDeleteLocally($subject);
172
                break;
173
174
            case self::DELETE_WORKING_COPY:
175
                return $this->canDeleteWorkingCopy($subject);
176
                break;
177
178
            case self::DUPLICATE:
179
                return $this->canDuplicate($subject);
180
                break;
181
182
            case self::RELEASE_PUBLISH:
183
                return $this->canReleasePublish($subject);
184
                break;
185
186
            case self::RELEASE_ACTIVATE:
187
                return $this->canReleaseActivate($subject);
188
                break;
189
190
            case self::REGISTER:
191
                return $this->canRegister($subject);
192
                break;
193
194
            case self::SHOW_DETAILS:
195
                return $this->canShowDetails($subject);
196
                break;
197
198
            case self::CANCEL_LIST_TASK:
199
                return $this->defaultAccess();
200
                break;
201
202
            case self::UPLOAD_FILES:
203
                return $this->canUpdate($subject);
204
                break;
205
206
            case self::EDIT:
207
                return $this->canEdit($subject);
208
                break;
209
210
            case self::POSTPONE:
211
                return $this->canPostpone($subject);
212
                break;
213
214
            case self::DOUBLET_CHECK:
215
                return $this->librarianOnly();
216
                break;
217
218
            case self::SUGGEST_RESTORE:
219
                return $this->canSuggestRestore($subject);
220
                break;
221
222
            case self::SUGGEST_MODIFICATION:
223
                return $this->canSuggestModification($subject);
224
                break;
225
226
            case self::SUGGESTION_ACCEPT:
227
                return $this->canSuggestionAccept($subject);
228
                break;
229
        }
230
231
        /** @var $logger \TYPO3\CMS\Core\Log\Logger */
232
        $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
233
        $logger->warning('DocumentVoter::voteOnAttribute(): Unknown attribute. This code should not be reached!',
234
            [
235
                'attribute' => $attribute
236
            ]
237
        );
238
239
        return false;
240
    }
241
242
    /**
243
     * @return bool
244
     */
245
    protected function defaultAccess()
246
    {
247
        return (
248
            $this->security->getUserRole() === Security::ROLE_LIBRARIAN ||
249
            $this->security->getUserRole() === Security::ROLE_RESEARCHER
250
        );
251
    }
252
253
    /**
254
     * @return bool
255
     */
256
    protected function librarianOnly()
257
    {
258
        return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
259
    }
260
261
    /**
262
     * @param \EWW\Dpf\Domain\Model\Document $document
263
     * @return bool
264
     */
265
    protected function canDiscard($document)
266
    {
267
        if ($this->isDocumentLocked($document)) {
268
            return FALSE;
269
        }
270
271
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_DISCARD)) {
0 ignored issues
show
Bug introduced by
The method can() does not exist on EWW\Dpf\Domain\Workflow\DocumentWorkflow. ( Ignorable by Annotation )

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

271
        if ($this->workflow->/** @scrutinizer ignore-call */ can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_DISCARD)) {

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...
272
            return (
273
                $this->security->getUserRole() === Security::ROLE_LIBRARIAN ||
274
                (
275
                    $document->getCreator() === $this->security->getUser()->getUid() &&
276
                    $document->getState() === DocumentWorkflow::STATE_REGISTERED_NONE
277
                )
278
            );
279
280
        }
281
282
        return FALSE;
283
    }
284
285
    /**
286
     * @param \EWW\Dpf\Domain\Model\Document $document
287
     * @return bool
288
     */
289
    protected function canShowDetails($document)
290
    {
291
        if ($this->security->getUserRole() === Security::ROLE_LIBRARIAN) {
292
            return (
293
                $document->getState() !== DocumentWorkflow::STATE_NEW_NONE ||
294
                $document->getCreator() === $this->security->getUser()->getUid()
295
            );
296
        }
297
298
        if ($this->security->getUserRole() === Security::ROLE_RESEARCHER) {
299
            return (
300
                $document->getCreator() === $this->security->getUser()->getUid() ||
301
                (
302
                    $document->getState() !== DocumentWorkflow::STATE_NEW_NONE &&
303
                    $document->getState() !== DocumentWorkflow::STATE_DISCARDED_NONE &&
304
                    $document->getState() !== DocumentWorkflow::STATE_NONE_DELETED
305
                )
306
            );
307
        }
308
309
        return FALSE;
310
    }
311
312
    /**
313
     * @param \EWW\Dpf\Domain\Model\Document $document
314
     * @return bool
315
     */
316
    protected function canRegister($document)
317
    {
318
        if (
319
            $this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_REGISTER) &&
320
            $document->getCreator() === $this->security->getUser()->getUid()
321
        ) {
322
           return TRUE;
323
        }
324
325
        return FALSE;
326
    }
327
328
329
    /**
330
     * @param \EWW\Dpf\Domain\Model\Document $document
331
     * @return bool
332
     */
333
    protected function canReleasePublish($document)
334
    {
335
        if ($this->isDocumentLocked($document)) {
336
            return FALSE;
337
        }
338
339
        if ($this->security->getUserRole() === Security::ROLE_LIBRARIAN) {
340
            return $this->workflow->can($document,
341
                \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_RELEASE_PUBLISH);
342
        }
343
344
        return FALSE;
345
    }
346
347
348
    /**
349
     * @param \EWW\Dpf\Domain\Model\Document $document
350
     * @return bool
351
     */
352
    protected function canReleaseActivate($document)
353
    {
354
        if ($this->isDocumentLocked($document)) {
355
            return FALSE;
356
        }
357
358
        if ($this->security->getUserRole() === Security::ROLE_LIBRARIAN) {
359
            return $this->workflow->can($document,
360
                \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_RELEASE_ACTIVATE);
361
        }
362
363
        return FALSE;
364
    }
365
366
    /**
367
     * @param \EWW\Dpf\Domain\Model\Document $document
368
     * @return bool
369
     */
370
    protected function canDeleteLocally($document)
371
    {
372
        if ($this->isDocumentLocked($document)) {
373
            return FALSE;
374
        }
375
376
        if ($document->isSuggestion()) {
377
            return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
378
        }
379
380
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_DELETE_LOCALLY)) {
381
            return $document->getCreator() === $this->security->getUser()->getUid();
382
        }
383
384
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_DELETE_DISCARDED)) {
385
            return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
386
        }
387
388
        return FALSE;
389
    }
390
391
392
    /**
393
     * @param \EWW\Dpf\Domain\Model\Document $document
394
     * @return bool
395
     */
396
    protected function canDeleteWorkingCopy($document)
397
    {
398
        if ($document->isTemporary() || $this->isDocumentLocked($document)) {
399
            return FALSE;
400
        }
401
402
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_DELETE_WORKING_COPY)) {
403
            return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
404
        }
405
406
        return FALSE;
407
    }
408
409
410
    /**
411
     * @param \EWW\Dpf\Domain\Model\Document $document
412
     * @return bool
413
     */
414
    protected function canEdit($document)
415
    {
416
        if ($this->isDocumentLocked($document)) {
417
            return FALSE;
418
        }
419
420
        if ($this->security->getUserRole() === Security::ROLE_LIBRARIAN) {
421
            return (
422
                $document->getState() !== DocumentWorkflow::STATE_NEW_NONE ||
423
                $document->getCreator() === $this->security->getUser()->getUid()
424
            );
425
        }
426
427
        if ($document->getCreator() === $this->security->getUser()->getUid()) {
428
            return (
429
                $document->getState() === DocumentWorkflow::STATE_NEW_NONE ||
430
                $document->getState() === DocumentWorkflow::STATE_REGISTERED_NONE
431
            );
432
        }
433
434
        return FALSE;
435
    }
436
437
438
    /**
439
     * @param \EWW\Dpf\Domain\Model\Document $document
440
     * @return bool
441
     */
442
    protected function canUpdate($document)
443
    {
444
        if ($this->isDocumentLocked($document)) {
445
            return FALSE;
446
        }
447
448
        if ($this->security->getUserRole() === Security::ROLE_LIBRARIAN) {
449
            return (
450
                $document->getState() !== DocumentWorkflow::STATE_NEW_NONE ||
451
                $document->getCreator() === $this->security->getUser()->getUid()
452
            );
453
        }
454
455
456
        if ($document->getCreator() === $this->security->getUser()->getUid()) {
457
            return (
458
                $document->getState() === DocumentWorkflow::STATE_NEW_NONE ||
459
                $document->getState() === DocumentWorkflow::STATE_REGISTERED_NONE
460
            );
461
        }
462
463
        return false;
464
    }
465
466
    /**
467
     * @param \EWW\Dpf\Domain\Model\Document $document
468
     * @return bool
469
     */
470
    protected function canSuggestRestore($document)
471
    {
472
        if ($this->security->getUserRole() === Security::ROLE_RESEARCHER) {
473
474
            $objectManager =GeneralUtility::makeInstance(ObjectManager::class);
475
            $documentRepository = $objectManager->get(DocumentRepository::class);
476
477
            $linkedDocument = $documentRepository->findOneByLinkedUid($document->getUid());
478
479
            if (!$linkedDocument && $document->getObjectIdentifier()) {
480
                $linkedDocument = $documentRepository->findOneByLinkedUid($document->getObjectIdentifier());
481
            }
482
483
            return (
484
                $document->getState() === DocumentWorkflow::STATE_DISCARDED_NONE ||
485
                $document->getState() === DocumentWorkflow::STATE_NONE_DELETED
486
            ) && !$linkedDocument;
487
        }
488
489
        return FALSE;
490
    }
491
492
    /**
493
     * @param \EWW\Dpf\Domain\Model\Document $document
494
     * @return bool
495
     */
496
    protected function canSuggestModification($document)
497
    {
498
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
499
        $documentRepository = $objectManager->get(DocumentRepository::class);
500
501
        $linkedDocument = $documentRepository->findOneByLinkedUid($document->getUid());
502
        if (!$linkedDocument && $document->getObjectIdentifier()) {
503
            $linkedDocument = $documentRepository->findOneByLinkedUid($document->getObjectIdentifier());
504
        }
505
506
        if ($this->security->getUserRole() === Security::ROLE_RESEARCHER) {
507
            return (
508
                (
509
                    $document->getCreator() !== $this->security->getUser()->getUid() &&
510
                    $document->getState() === DocumentWorkflow::STATE_REGISTERED_NONE
511
                ) ||
512
                (
513
                    $document->getState() !== DocumentWorkflow::STATE_NEW_NONE &&
514
                    $document->getState() !== DocumentWorkflow::STATE_REGISTERED_NONE
515
                )
516
            ) && !$linkedDocument;
517
        }
518
519
        return FALSE;
520
    }
521
522
    /**
523
     * @param \EWW\Dpf\Domain\Model\Document $document
524
     * @return bool
525
     */
526
    protected function canSuggestionAccept($document)
0 ignored issues
show
Unused Code introduced by
The parameter $document is not used and could be removed. ( Ignorable by Annotation )

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

526
    protected function canSuggestionAccept(/** @scrutinizer ignore-unused */ $document)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
527
    {
528
        // TODO: What if a document should be restored?
529
530
        return $this->librarianOnly();
531
    }
532
533
    /**
534
     * @param \EWW\Dpf\Domain\Model\Document $document
535
     * @return bool
536
     */
537
    protected function canPostpone($document)
538
    {
539
        if ($this->isDocumentLocked($document)) {
540
            return FALSE;
541
        }
542
543
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_POSTPONE)) {
544
            return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
545
        }
546
547
        return FALSE;
548
    }
549
550
551
    /**
552
     * @param \EWW\Dpf\Domain\Model\Document $document
553
     * @return bool
554
     */
555
    protected function canCreateRegister($document)
556
    {
557
        if ($this->security->getUserRole()) {
558
            return FALSE;
559
        }
560
561
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_CREATE_REGISTER)) {
562
            return TRUE;
563
        }
564
565
        return FALSE;
566
    }
567
568
    /**
569
     * @param \EWW\Dpf\Domain\Model\Document $document
570
     * @return bool
571
     */
572
    protected function canDuplicate($document)
573
    {
574
        if ($this->security->getUserRole() === Security::ROLE_LIBRARIAN) {
575
            return (
576
                $document->getState() !== DocumentWorkflow::STATE_NEW_NONE ||
577
                $document->getCreator() === $this->security->getUser()->getUid()
578
            );
579
        }
580
581
        if ($this->security->getUserRole() === Security::ROLE_RESEARCHER) {
582
            return (
583
                $document->getCreator() === $this->security->getUser()->getUid() ||
584
                $document->getState() !== DocumentWorkflow::STATE_NEW_NONE
585
            );
586
        }
587
588
        return FALSE;
589
    }
590
591
    /**
592
     * @param \EWW\Dpf\Domain\Model\Document $document
593
     * @return bool
594
     */
595
    protected function isDocumentLocked($document)
596
    {
597
        $identifier = $document->getObjectIdentifier()? $document->getObjectIdentifier() : $document->getUid();
598
        return $this->editingLockService->isLocked($identifier, $this->security->getUser()->getUid());
599
    }
600
601
}