Passed
Push — master ( 4a9186...cf0fc9 )
by vistart
03:05
created

DbManager::revokeFailedAssignment()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
ccs 9
cts 10
cp 0.9
rs 9.2
cc 4
eloc 10
nc 4
nop 1
crap 4.016
1
<?php
2
3
/**
4
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 - 2017 vistart
10
 * @license https://vistart.me/license/
11
 */
12
13
namespace rhosocial\user\rbac;
14
15
use rhosocial\user\User;
16
use yii\db\Query;
17
use yii\rbac\Item;
18
19
/**
20
 * This DbManager replaces the UserID of original DbManager with the UserGUID.
21
 *
22
 * @see User
23
 * @version 1.0
24
 * @author vistart <[email protected]>
25
 */
26
class DbManager extends \yii\rbac\DbManager
27
{
28
    /**
29
     * @inheritdoc
30
     */
31
    protected function addItem($item)
32
    {
33
        $time = date('Y-m-d H:i:s');
34
        if ($item->createdAt === null) {
35
            $item->createdAt = $time;
0 ignored issues
show
Documentation Bug introduced by
The property $createdAt was declared of type integer, but $time is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
36
        }
37
        if ($item->updatedAt === null) {
38
            $item->updatedAt = $time;
0 ignored issues
show
Documentation Bug introduced by
The property $updatedAt was declared of type integer, but $time is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
39
        }
40
        $this->db->createCommand()
41
            ->insert($this->itemTable, [
42
                'name' => $item->name,
43
                'type' => $item->type,
44
                'description' => $item->description,
45
                'rule_name' => $item->ruleName,
46
                'data' => $item->data === null ? null : serialize($item->data),
47
                'created_at' => $item->createdAt,
48
                'updated_at' => $item->updatedAt,
49
            ])->execute();
50
51
        $this->invalidateCache();
52
53
        return true;
54
    }
55
    
56
    /**
57
     * @inheritdoc
58
     */
59
    protected function updateItem($name, $item)
60
    {
61
        if ($item->name !== $name && !$this->supportsCascadeUpdate()) {
62
            $this->db->createCommand()
63
                ->update($this->itemChildTable, ['parent' => $item->name], ['parent' => $name])
64
                ->execute();
65
            $this->db->createCommand()
66
                ->update($this->itemChildTable, ['child' => $item->name], ['child' => $name])
67
                ->execute();
68
            $this->db->createCommand()
69
                ->update($this->assignmentTable, ['item_name' => $item->name], ['item_name' => $name])
70
                ->execute();
71
        }
72
73
        $item->updatedAt = date('Y-m-d H:i:s');
0 ignored issues
show
Documentation Bug introduced by
The property $updatedAt was declared of type integer, but date('Y-m-d H:i:s') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
74
75
        $this->db->createCommand()
76
            ->update($this->itemTable, [
77
                'name' => $item->name,
78
                'description' => $item->description,
79
                'rule_name' => $item->ruleName,
80
                'data' => $item->data === null ? null : serialize($item->data),
81
                'updated_at' => $item->updatedAt,
82
            ], [
83
                'name' => $name,
84
            ])->execute();
85
86
        $this->invalidateCache();
87
88
        return true;
89
    }
90
91
    /**
92
     * @inheritdoc
93
     */
94
    protected function addRule($rule)
95
    {
96
        $time = date('Y-m-d H:i:s');
97
        if ($rule->createdAt === null) {
98
            $rule->createdAt = $time;
0 ignored issues
show
Documentation Bug introduced by
The property $createdAt was declared of type integer, but $time is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
99
        }
100
        if ($rule->updatedAt === null) {
101
            $rule->updatedAt = $time;
0 ignored issues
show
Documentation Bug introduced by
The property $updatedAt was declared of type integer, but $time is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
102
        }
103
        $this->db->createCommand()
104
            ->insert($this->ruleTable, [
105
                'name' => $rule->name,
106
                'data' => serialize($rule),
107
                'created_at' => $rule->createdAt,
108
                'updated_at' => $rule->updatedAt,
109
            ])->execute();
110
111
        $this->invalidateCache();
112
113
        return true;
114
    }
115
    
116
    /**
117
     * @inheritdoc
118
     */
119
    protected function updateRule($name, $rule)
120
    {
121
        if ($rule->name !== $name && !$this->supportsCascadeUpdate()) {
122
            $this->db->createCommand()
123
                ->update($this->itemTable, ['rule_name' => $rule->name], ['rule_name' => $name])
124
                ->execute();
125
        }
126
127
        $rule->updatedAt = date('Y-m-d H:i:s');
0 ignored issues
show
Documentation Bug introduced by
The property $updatedAt was declared of type integer, but date('Y-m-d H:i:s') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
128
129
        $this->db->createCommand()
130
            ->update($this->ruleTable, [
131
                'name' => $rule->name,
132
                'data' => serialize($rule),
133
                'updated_at' => $rule->updatedAt,
134
            ], [
135
                'name' => $name,
136
            ])->execute();
137
138
        $this->invalidateCache();
139
140
        return true;
141
    }
142
    
143
    /**
144
     * 
145
     * @param string|User $userGuid
146
     * @return array
147
     */
148 1
    public function getRolesByUser($userGuid) {
149 1
        if (!isset($userGuid) || $userGuid === '') {
150 1
            return [];
151
        }
152
        
153 1
        if ($userGuid instanceof User) {
154 1
            $userGuid = $userGuid->getGUID();
155
        }
156
157 1
        $query = (new Query)->select('b.*')
158 1
            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])
159 1
            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')
160 1
            ->andWhere(['a.user_guid' => (string) $userGuid])
161 1
            ->andWhere(['b.type' => Item::TYPE_ROLE]);
162
163 1
        $roles = [];
164 1
        foreach ($query->all($this->db) as $row) {
165 1
            $roles[$row['name']] = $this->populateItem($row);
166
        }
167 1
        return $roles;
168
    }
169
170
    /**
171
     * Returns all permissions that are directly assigned to user.
172
     * @param string|User $userGuid the user GUID (see [[\rhosocial\user\User::guid]])
173
     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.
174
     */
175 3
    protected function getDirectPermissionsByUser($userGuid)
176
    {
177 3
        $query = (new Query)->select('b.*')
178 3
            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])
179 3
            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')
180 3
            ->andWhere(['a.user_guid' => (string) $userGuid])
181 3
            ->andWhere(['b.type' => Item::TYPE_PERMISSION]);
182
183 3
        $permissions = [];
184 3
        foreach ($query->all($this->db) as $row) {
185
            $permissions[$row['name']] = $this->populateItem($row);
186
        }
187 3
        return $permissions;
188
    }
189
190
    /**
191
     * Returns all permissions that the user inherits from the roles assigned to him.
192
     * @param string|User $userGuid the user GUID (see [[\rhosocial\user\User::guid]])
193
     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.
194
     */
195 3
    protected function getInheritedPermissionsByUser($userGuid)
196
    {
197 3
        $query = (new Query)->select('item_name')
198 3
            ->from($this->assignmentTable)
199 3
            ->where(['user_guid' => (string) $userGuid]);
200
201 3
        $childrenList = $this->getChildrenList();
202 3
        $result = [];
203 3
        foreach ($query->column($this->db) as $roleName) {
204 1
            $this->getChildrenRecursive($roleName, $childrenList, $result);
205
        }
206
207 3
        if (empty($result)) {
208 2
            return [];
209
        }
210
211 1
        $query = (new Query)->from($this->itemTable)->where([
212 1
            'type' => Item::TYPE_PERMISSION,
213 1
            'name' => array_keys($result),
214
        ]);
215 1
        $permissions = [];
216 1
        foreach ($query->all($this->db) as $row) {
217 1
            $permissions[$row['name']] = $this->populateItem($row);
218
        }
219 1
        return $permissions;
220
    }
221
222
    /**
223
     * @inheritdoc
224
     */
225 10
    public function getAssignment($roleName, $userGuid)
226
    {
227 10
        if (empty($userGuid)) {
228 1
            return null;
229
        }
230
        
231 10
        if ($userGuid instanceof User) {
232 9
            $userGuid = $userGuid->getGUID();
233
        }
234
235 10
        $row = (new Query)->from($this->assignmentTable)
236 10
            ->where(['user_guid' => (string) $userGuid, 'item_name' => $roleName])
237 10
            ->one($this->db);
238
239 10
        if ($row === false) {
240 1
            return null;
241
        }
242
        
243 9
        $assignment = new Assignment([
244 9
            'userGuid' => $row['user_guid'],
245 9
            'roleName' => $row['item_name'],
246 9
            'createdAt' => $row['created_at'],
247 9
            'failedAt' => $row['failed_at'],
248
        ]);
249
        
250 9
        if ($this->revokeFailedAssignment($assignment)) {
251 2
            return null;
252
        }
253
254 7
        return $assignment;
255
    }
256
    
257
    /**
258
     * Revoke failed assignment.
259
     * If assignment's `failedAt` attribute is `null`, false will be given directly.
260
     * @param Assignment $assignment
261
     * @return boolean
262
     */
263 9
    protected function revokeFailedAssignment(Assignment $assignment)
264
    {
265 9
        if ($assignment->failedAt === null) {
266 7
            return false;
267
        }
268 2
        if (strtotime($assignment->failedAt) < strtotime(date('Y-m-d H:i:s'))) {
269 2
            $role = $this->getRole($assignment->roleName);
270 2
            if (!$role) {
271 1
                return true;
272
            }
273 1
            $this->revoke($role, $assignment->userGuid);
0 ignored issues
show
Compatibility introduced by
$role of type object<yii\rbac\Item> is not a sub-type of object<yii\rbac\Role>. It seems like you assume a child class of the class yii\rbac\Item to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like $assignment->userGuid can also be of type object<rhosocial\user\User>; however, rhosocial\user\rbac\DbManager::revoke() does only seem to accept string|integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
274 1
            return true;
275
        }
276
        return false;
277
    }
278
279
    /**
280
     * @inheritdoc
281
     */
282 7
    public function getAssignments($userGuid)
283
    {
284 7
        if (empty($userGuid)) {
285 1
            return [];
286
        }
287
        
288 7
        if ($userGuid instanceof User) {
289 1
            $userGuid = $userGuid->getGUID();
290
        }
291
292 7
        $query = (new Query)
293 7
            ->from($this->assignmentTable)
294 7
            ->where(['user_guid' => (string) $userGuid]);
295
296 7
        $assignments = [];
297 7
        foreach ($query->all($this->db) as $row) {
298 6
            $assignment = new Assignment([
299 6
                'userGuid' => $row['user_guid'],
300 6
                'roleName' => $row['item_name'],
301 6
                'createdAt' => $row['created_at'],
302 6
                'failedAt' => $row['failed_at'],
303
            ]);
304 6
            if ($this->revokeFailedAssignment($assignment)) {
305
                continue;
306
            }
307 6
            $assignments[$row['item_name']] = $assignment;
308
        }
309
310 7
        return $assignments;
311
    }
312
313
    /**
314
     * @inheritdoc
315
     */
316 14
    public function assign($role, $userGuid, $failedAt = null)
317
    {
318 14
        $assignment = new Assignment([
319 14
            'userGuid' => $userGuid,
320 14
            'roleName' => $role->name,
321 14
            'createdAt' => date('Y-m-d H:i:s'),
322 14
            'failedAt' => empty($failedAt) ? null : $failedAt,
323
        ]);
324
325 14
        $this->db->createCommand()
326 14
            ->insert($this->assignmentTable, [
327 14
                'user_guid' => $assignment->userGuid,
328 14
                'item_name' => $assignment->roleName,
329 14
                'created_at' => $assignment->createdAt,
330 14
                'failed_at' => $assignment->failedAt,
331 14
            ])->execute();
332
333 14
        return $assignment;
334
    }
335
336
    /**
337
     * @inheritdoc
338
     */
339 2
    public function revoke($role, $userGuid)
340
    {
341 2
        if (empty($userGuid)) {
342 1
            return false;
343
        }
344
        
345 2
        if ($userGuid instanceof User) {
346 1
            $userGuid = $userGuid->getGUID();
347
        }
348
349 2
        return $this->db->createCommand()
350 2
            ->delete($this->assignmentTable, ['user_guid' => (string) $userGuid, 'item_name' => $role->name])
351 2
            ->execute() > 0;
352
    }
353
354
    /**
355
     * @inheritdoc
356
     */
357 1
    public function revokeAll($userGuid)
358
    {
359 1
        if (empty($userGuid)) {
360 1
            return false;
361
        }
362
        
363 1
        if ($userGuid instanceof User) {
364 1
            $userGuid = $userGuid->getGUID();
365
        }
366
367 1
        return $this->db->createCommand()
368 1
            ->delete($this->assignmentTable, ['user_guid' => (string) $userGuid])
369 1
            ->execute() > 0;
370
    }
371
372
    /**
373
     * Returns all role assignment information for the specified role.
374
     * @param string $roleName
375
     * @return Assignment[] the assignments. An empty array will be
376
     * returned if role is not assigned to any user.
377
     * @since 2.0.7
378
     */
379 1
    public function getUserGuidsByRole($roleName)
380
    {
381 1
        if (empty($roleName)) {
382 1
            return [];
383
        }
384
385 1
        return (new Query)->select('[[user_guid]]')
386 1
            ->from($this->assignmentTable)
387 1
            ->where(['item_name' => $roleName])->column($this->db);
388
    }
389
}