Passed
Push — 4-cactus ( b790f6...5d8cf7 )
by Stefano
03:27
created

RolesUsersTable::canHandle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 16
rs 9.9
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2017 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
14
namespace BEdita\Core\Model\Table;
15
16
use BEdita\Core\Exception\ImmutableResourceException;
17
use BEdita\Core\Utility\LoggedUser;
18
use Cake\Datasource\EntityInterface;
19
use Cake\Event\Event;
20
use Cake\Event\EventInterface;
21
use Cake\Http\Exception\ForbiddenException;
22
use Cake\ORM\RulesChecker;
23
use Cake\ORM\Table;
24
use Cake\Utility\Hash;
25
use Cake\Validation\Validator;
26
27
/**
28
 * RolesUsers Model
29
 *
30
 * @property \Cake\ORM\Association\BelongsTo $Roles
31
 * @property \Cake\ORM\Association\BelongsTo $Users
32
 * @method \BEdita\Core\Model\Entity\RolesUser get($primaryKey, $options = [])
33
 * @method \BEdita\Core\Model\Entity\RolesUser newEntity($data = null, array $options = [])
34
 * @method \BEdita\Core\Model\Entity\RolesUser[] newEntities(array $data, array $options = [])
35
 * @method \BEdita\Core\Model\Entity\RolesUser|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
36
 * @method \BEdita\Core\Model\Entity\RolesUser patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
37
 * @method \BEdita\Core\Model\Entity\RolesUser[] patchEntities($entities, array $data, array $options = [])
38
 * @method \BEdita\Core\Model\Entity\RolesUser findOrCreate($search, callable $callback = null, $options = [])
39
 */
40
class RolesUsersTable extends Table
41
{
42
    /**
43
     * {@inheritDoc}
44
     *
45
     * @codeCoverageIgnore
46
     */
47
    public function initialize(array $config): void
48
    {
49
        parent::initialize($config);
50
51
        $this->setTable('roles_users');
52
        $this->setPrimaryKey('id');
53
        $this->setDisplayField('id');
54
55
        $this->belongsTo('Roles', [
56
            'foreignKey' => 'role_id',
57
            'joinType' => 'INNER',
58
        ]);
59
        $this->belongsTo('Users', [
60
            'foreignKey' => 'user_id',
61
            'joinType' => 'INNER',
62
        ]);
63
    }
64
65
    /**
66
     * {@inheritDoc}
67
     *
68
     * @codeCoverageIgnore
69
     */
70
    public function validationDefault(Validator $validator): Validator
71
    {
72
        $validator
73
            ->integer('id')
74
            ->allowEmptyString('id', null, 'create');
75
76
        return $validator;
77
    }
78
79
    /**
80
     * {@inheritDoc}
81
     *
82
     * @codeCoverageIgnore
83
     */
84
    public function buildRules(RulesChecker $rules): RulesChecker
85
    {
86
        $rules->add($rules->isUnique(['role_id', 'user_id']));
87
        $rules->add($rules->existsIn(['role_id'], 'Roles'));
88
        $rules->add($rules->existsIn(['user_id'], 'Users'));
89
90
        return $rules;
91
    }
92
93
    /**
94
     * Before delete checks: if record is not deletable, raise a ForbiddenException or ImmutableResourceException
95
     *
96
     * @param \Cake\Event\Event $event The beforeSave event that was fired
97
     * @param \Cake\Datasource\EntityInterface $entity the entity that is going to be saved
98
     * @return void
99
     * @throws \BEdita\Core\Exception\ImmutableResourceException; if entity is not deletable
100
     */
101
    public function beforeDelete(Event $event, EntityInterface $entity)
0 ignored issues
show
Unused Code introduced by
The parameter $event 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

101
    public function beforeDelete(/** @scrutinizer ignore-unused */ Event $event, EntityInterface $entity)

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...
102
    {
103
        if ($entity->role_id === RolesTable::ADMIN_ROLE && $entity->user_id === UsersTable::ADMIN_USER) {
0 ignored issues
show
Bug introduced by
Accessing user_id on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
Bug introduced by
Accessing role_id on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
104
            throw new ImmutableResourceException(__d('bedita', 'Could not update relationship for users/roles for ADMIN_USER and ADMIN_ROLE'));
105
        }
106
        if (!$this->canHandle($entity->role_id)) {
107
            throw new ForbiddenException(__d('bedita', 'Could not update role. Insufficient priority'));
108
        }
109
    }
110
111
    /**
112
     * Before save checks: if record is not changeable, raise a ForbiddenException
113
     *
114
     * @param \Cake\Event\EventInterface $event The beforeSave event that was fired
115
     * @param \Cake\Datasource\EntityInterface $entity the entity that is going to be saved
116
     * @return void
117
     * @throws \Cake\Http\Exception\ForbiddenException; if logged user cannot modify user role
118
     */
119
    public function beforeSave(EventInterface $event, EntityInterface $entity)
0 ignored issues
show
Unused Code introduced by
The parameter $event 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

119
    public function beforeSave(/** @scrutinizer ignore-unused */ EventInterface $event, EntityInterface $entity)

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...
120
    {
121
        if (!$this->canHandle($entity->role_id)) {
0 ignored issues
show
Bug introduced by
Accessing role_id on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
122
            throw new ForbiddenException(__d('bedita', 'Could not update role. Insufficient priority'));
123
        }
124
    }
125
126
    /**
127
     * Check that logged user can add or remove associations to a role.
128
     * Logged user roles min priority should be less or equal to the role priority.
129
     *
130
     * @param int $roleId The role ID to check againt logged user roles priorities
131
     * @return bool
132
     */
133
    protected function canHandle(int $roleId): bool
134
    {
135
        $user = LoggedUser::getUser();
136
        $ids = (array)Hash::extract($user, 'roles.{n}.id');
137
        if (empty($ids)) {
138
            return false;
139
        }
140
        $query = $this->Roles->find('list', ['valueField' => 'min_value'])
141
            ->where(['id IN' => $ids]);
142
        $priorityUser = $query->select([
143
                'min_value' => $query->func()->min($this->Roles->aliasField('priority')),
144
            ])
145
            ->first();
146
        $priorityRole = $this->Roles->get($roleId)->get('priority');
147
148
        return $priorityUser <= $priorityRole;
149
    }
150
}
151