Passed
Pull Request — master (#155)
by
unknown
09:48
created

DocumentVoter   F

Complexity

Total Complexity 107

Size/Duplication

Total Lines 578
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 248
c 6
b 0
f 0
dl 0
loc 578
rs 2
wmc 107

21 Methods

Rating   Name   Duplication   Size   Complexity  
A canDiscard() 0 19 5
A canDeleteLocally() 0 19 6
A supports() 0 11 4
A canReleasePublish() 0 12 4
A canEdit() 0 21 6
A __construct() 0 3 1
C canShowDetails() 0 26 12
A defaultAccess() 0 5 2
A canReleaseActivate() 0 19 5
A canUpdate() 0 21 6
A canDeleteWorkingCopy() 0 11 4
A isDocumentLocked() 0 5 2
A canCreateRegister() 0 11 3
A getAttributes() 0 26 1
A canRegister() 0 10 3
A canPostpone() 0 11 3
B canSuggestModification() 0 24 8
D voteOnAttribute() 0 102 24
A canSuggestRestore() 0 19 6
A canSuggestionAccept() 0 5 1
A librarianOnly() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like DocumentVoter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DocumentVoter, and based on these observations, apply Extract Interface, too.

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
23
class DocumentVoter extends Voter
24
{
25
    const CREATE = "DOCUMENT_CREATE";
26
    const CREATE_REGISTER = "DOCUMENT_CREATE_REGISTER";
27
    const UPDATE = "DOCUMENT_UPDATE";
28
    const LIST = "DOCUMENT_LIST";
29
    const LIST_REGISTERED = "DOCUMENT_LIST_REGISTERED";
30
    const LIST_IN_PROGRESS = "DOCUMENT_LIST_IN_PROGRESS";
31
    const DISCARD = "DOCUMENT_DISCARD";
32
    const DELETE_LOCALLY = "DOCUMENT_DELETE_LOCALLY";
33
    const DELETE_WORKING_COPY = "DOCUMENT_DELETE_WORKING_COPY";
34
    const DUPLICATE = "DOCUMENT_DUPLICATE";
35
    const RELEASE_PUBLISH = "DOCUMENT_RELEASE_PUBLISH";
36
//    const RELEASE_UPDATE = "DOCUMENT_RELEASE_UPDATE";
37
    const RELEASE_ACTIVATE = "DOCUMENT_RELEASE_ACTIVATE";
38
    const REGISTER = "DOCUMENT_REGISTER";
39
    const SHOW_DETAILS = "DOCUMENT_SHOW_DETAILS";
40
    const CANCEL_LIST_TASK = "DOCUMENT_CANCEL_LIST_TASK";
41
    const UPLOAD_FILES = "DOCUMENT_UPLOAD_FILES";
42
    const EDIT = "DOCUMENT_EDIT";
43
    const SUGGEST = "DOCUMENT_SUGGEST";
44
    const POSTPONE = "DOCUMENT_POSTPONE";
45
    const DOUBLET_CHECK = "DOCUMENT_DOUBLET_CHECK";
46
    const SUGGEST_RESTORE = "DOCUMENT_SUGGEST_RESTORE";
47
    const SUGGEST_MODIFICATION = "DOCUMENT_SUGGEST_MODIFICATION";
48
    const SUGGESTION_ACCEPT = "DOCUMENT_SUGGESTION_ACCEPT";
49
50
    /**
51
     * workflow
52
     *
53
     * @var DocumentWorkflow
54
     */
55
    protected $workflow;
56
57
    public function __construct()
58
    {
59
       $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...
60
    }
61
62
63
    /**
64
     * Returns all supported attributes.
65
     *
66
     * @return array
67
     */
68
    protected static function getAttributes()
69
    {
70
        return array(
71
            self::CREATE,
72
            self::CREATE_REGISTER,
73
            self::UPDATE,
74
            self::LIST,
75
            self::LIST_REGISTERED,
76
            self::LIST_IN_PROGRESS,
77
            self::DISCARD,
78
            self::DELETE_LOCALLY,
79
            self::DELETE_WORKING_COPY,
80
            self::DUPLICATE,
81
            self::RELEASE_PUBLISH,
82
//            self::RELEASE_UPDATE,
83
            self::RELEASE_ACTIVATE,
84
            self::REGISTER,
85
            self::SHOW_DETAILS,
86
            self::CANCEL_LIST_TASK,
87
            self::UPLOAD_FILES,
88
            self::EDIT,
89
            self::POSTPONE,
90
            self::DOUBLET_CHECK,
91
            self::SUGGEST_RESTORE,
92
            self::SUGGEST_MODIFICATION,
93
            self::SUGGESTION_ACCEPT
94
        );
95
    }
96
97
98
    /**
99
     * Determines if the voter supports the given attribute.
100
     *
101
     * @param string $attribute
102
     * @param mixed $subject
103
     * @return mixed
104
     */
105
    public static function supports($attribute, $subject = NULL)
106
    {
107
        if (!in_array($attribute, self::getAttributes())) {
108
            return FALSE;
109
        }
110
111
        if (!$subject instanceof Document && !is_null($subject)) {
112
            return FALSE;
113
        }
114
115
        return TRUE;
116
    }
117
118
    /**
119
     * Determines if access for the given attribute and subject is allowed.
120
     *
121
     * @param string $attribute
122
     * @param mixed $subject
123
     * @return mixed
124
     */
125
    public function voteOnAttribute($attribute, $subject = NULL)
126
    {
127
        if (!$subject instanceof Document) {
128
            return FALSE;
129
        }
130
131
        switch ($attribute) {
132
133
            case self::CREATE:
134
                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

134
                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...
135
                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...
136
137
            case self::CREATE_REGISTER:
138
                return $this->canCreateRegister($subject);
139
                break;
140
141
            case self::UPDATE:
142
                return $this->canUpdate($subject);
143
                break;
144
145
            case self::LIST:
146
                return $this->defaultAccess();
147
                break;
148
149
            case self::LIST_REGISTERED:
150
                return $this->defaultAccess();
151
                break;
152
153
            case self::LIST_IN_PROGRESS:
154
                return $this->defaultAccess();
155
                break;
156
157
            case self::DISCARD:
158
                return $this->canDiscard($subject);
159
                break;
160
161
            case self::DELETE_LOCALLY:
162
                return $this->canDeleteLocally($subject);
163
                break;
164
165
            case self::DELETE_WORKING_COPY:
166
                return $this->canDeleteWorkingCopy($subject);
167
                break;
168
169
            case self::DUPLICATE:
170
                return $this->librarianOnly();
171
                break;
172
173
            case self::RELEASE_PUBLISH:
174
                return $this->canReleasePublish($subject);
175
                break;
176
177
//            case self::RELEASE_UPDATE:
178
//                return $this->canReleaseUpdate($subject);
179
//                break;
180
181
            case self::RELEASE_ACTIVATE:
182
                return $this->canReleaseActivate($subject);
183
                break;
184
185
            case self::REGISTER:
186
                return $this->canRegister($subject);
187
                break;
188
189
            case self::SHOW_DETAILS:
190
                return $this->canShowDetails($subject);
191
                break;
192
193
            case self::CANCEL_LIST_TASK:
194
                return $this->defaultAccess();
195
                break;
196
197
            case self::UPLOAD_FILES:
198
                return $this->canUpdate($subject);
199
                break;
200
201
            case self::EDIT:
202
                return $this->canEdit($subject);
203
                break;
204
205
            case self::POSTPONE:
206
                return $this->canPostpone($subject);
207
                break;
208
209
            case self::DOUBLET_CHECK:
210
                return $this->librarianOnly();
211
                break;
212
213
            case self::SUGGEST_RESTORE:
214
                return $this->canSuggestRestore($subject);
215
                break;
216
217
            case self::SUGGEST_MODIFICATION:
218
                return $this->canSuggestModification($subject);
219
                break;
220
221
            case self::SUGGESTION_ACCEPT:
222
                return $this->canSuggestionAccept($subject);
223
                break;
224
        }
225
226
        throw new \Exception('An unexpected error occurred!');
227
    }
228
229
    /**
230
     * @return bool
231
     */
232
    protected function defaultAccess()
233
    {
234
        return (
235
            $this->security->getUserRole() === Security::ROLE_LIBRARIAN ||
236
            $this->security->getUserRole() === Security::ROLE_RESEARCHER
237
        );
238
    }
239
240
    /**
241
     * @return bool
242
     */
243
    protected function librarianOnly()
244
    {
245
        return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
246
    }
247
248
    /**
249
     * @param \EWW\Dpf\Domain\Model\Document $document
250
     * @return bool
251
     */
252
    protected function canDiscard($document)
253
    {
254
        if ($this->isDocumentLocked($document)) {
255
            return FALSE;
256
        }
257
258
        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

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

550
    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...
551
    {
552
        // TODO: What if a document should be restored?
553
554
        return $this->librarianOnly();
555
    }
556
557
    /**
558
     * @param \EWW\Dpf\Domain\Model\Document $document
559
     * @return bool
560
     */
561
    protected function canPostpone($document)
562
    {
563
        if ($this->isDocumentLocked($document)) {
564
            return FALSE;
565
        }
566
567
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_POSTPONE)) {
568
            return $this->security->getUserRole() === Security::ROLE_LIBRARIAN;
569
        }
570
571
        return FALSE;
572
    }
573
574
575
    /**
576
     * @param \EWW\Dpf\Domain\Model\Document $document
577
     * @return bool
578
     */
579
    protected function canCreateRegister($document)
580
    {
581
        if ($this->security->getUserRole()) {
582
            return FALSE;
583
        }
584
585
        if ($this->workflow->can($document, \EWW\Dpf\Domain\Workflow\DocumentWorkflow::TRANSITION_CREATE_REGISTER)) {
586
            return TRUE;
587
        }
588
589
        return FALSE;
590
    }
591
592
    /**
593
     * @param \EWW\Dpf\Domain\Model\Document $document
594
     * @return bool
595
     */
596
    protected function isDocumentLocked($document)
597
    {
598
        return (
599
            $document->getEditorUid() !== 0 &&
600
            $document->getEditorUid() !== $this->security->getUser()->getUid()
601
        );
602
    }
603
604
}