Test Failed
Push — master ( 894c40...e5d2d2 )
by Julien
11:34
created

Security   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 45
c 1
b 0
f 0
dl 0
loc 87
ccs 0
cts 29
cp 0
rs 10
wmc 17

3 Methods

Rating   Name   Duplication   Size   Complexity  
A isAllowed() 0 31 4
A __construct() 0 6 1
C notify() 0 23 12
1
<?php
2
3
/**
4
 * This file is part of the Zemit Framework.
5
 *
6
 * (c) Zemit Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Zemit\Mvc\Model\Behavior;
13
14
use Phalcon\Di;
15
use Phalcon\Text;
16
use Phalcon\Messages\Message;
17
use Phalcon\Mvc\ModelInterface;
18
use Phalcon\Mvc\Model\Behavior;
19
use Zemit\Config\Config;
20
use Zemit\Identity;
21
22
/**
23
 * Allows to check if the current identity is allowed to run some model actions
24
 * this behavior will stop operations if not allowed
25
 */
26
class Security extends Behavior
27
{
28
    use SkippableTrait;
29
    
30
    protected Config $config;
31
    
32
    protected \Zemit\Security $security;
33
    
34
    protected Identity $identity;
35
    
36
    /**
37
     * @todo avoid using DI
38
     * @param array|null $options
39
     */
40
    public function __construct(?array $options = null)
41
    {
42
        parent::__construct($options);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type null; however, parameter $options of Phalcon\Mvc\Model\Behavior::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

42
        parent::__construct(/** @scrutinizer ignore-type */ $options);
Loading history...
43
        $this->config ??= Di::getDefault()->get('config');
44
        $this->security ??= Di::getDefault()->get('security');
45
        $this->identity ??= Di::getDefault()->get('identity');
46
    }
47
    
48
    /**
49
     * Handling security on some model events
50
     * - beforeCreate
51
     * - beforeUpdate
52
     * - beforeDelete
53
     * - beforeRestore
54
     *
55
     * {@inheritdoc}
56
     */
57
    public function notify(string $type, ModelInterface $model): bool
58
    {
59
        if (!$this->isEnabled()) {
60
            return true;
61
        }
62
        
63
        $this->security->getAcl();
64
        switch ($type) {
65
            case 'beforeFind': // @todo implement this
66
            case 'beforeFindFirst': // @todo implement this
67
            case 'beforeCount':  // @todo implement this
68
            case 'beforeSum': // @todo implement this
69
            case 'beforeAverage': // @todo implement this
70
            case 'beforeCreate':
71
            case 'beforeUpdate':
72
            case 'beforeDelete':
73
            case 'beforeRestore':
74
            case 'beforeReorder':
75
                $type = lcfirst(Text::camelize(str_replace(['before_', 'after_'], [null, null], Text::uncamelize($type))));
76
                return $this->isAllowed($type, $model);
77
        }
78
        
79
        return true;
80
    }
81
    
82
    public function isAllowed(string $type, ModelInterface $model): bool
83
    {
84
        $acl = $this->security->getAcl(['models', 'components']);
85
        $modelClass = get_class($model);
86
        
87
        // component not found
88
        if (!$acl->isComponent($modelClass)) {
89
            $model->appendMessage(new Message(
90
                'Model permission not found for `' . $modelClass . '`',
91
                'id',
92
                'NotFound',
93
                404
94
            ));
95
            return false;
96
        }
97
        
98
        // allowed for roles
99
        $roles = $this->identity->getAclRoles();
100
        foreach ($roles as $role) {
101
            if ($acl->isAllowed($role, $modelClass, $type)) {
102
                return true;
103
            }
104
        }
105
        
106
        $model->appendMessage(new Message(
107
            'Current identity forbidden to execute `' . $type . '` on `' . $modelClass . '`',
108
            'id',
109
            'NotFound',
110
            403
111
        ));
112
        return false;
113
    }
114
}
115