Completed
Push — master ( 349c2f...e64df6 )
by vistart
03:26
created

DbManager::getRolesByUser()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 21
rs 8.7624
c 1
b 0
f 1
ccs 14
cts 14
cp 1
cc 5
eloc 14
nc 5
nop 1
crap 5
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
18
/**
19
 * This DbManager replaces the UserID of original DbManager with the UserGUID.
20
 *
21
 * @see User
22
 * @version 1.0
23
 * @author vistart <[email protected]>
24
 */
25
class DbManager extends \yii\rbac\DbManager
26
{
27
    /**
28
     * @inheritdoc
29
     */
30
    protected function addItem($item)
31
    {
32
        $time = date('Y-m-d H:i:s');
33
        if ($item->createdAt === null) {
34
            $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...
35
        }
36
        if ($item->updatedAt === null) {
37
            $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...
38
        }
39
        $this->db->createCommand()
40
            ->insert($this->itemTable, [
41
                'name' => $item->name,
42
                'type' => $item->type,
43
                'description' => $item->description,
44
                'rule_name' => $item->ruleName,
45
                'data' => $item->data === null ? null : serialize($item->data),
46
                'created_at' => $item->createdAt,
47
                'updated_at' => $item->updatedAt,
48
            ])->execute();
49
50
        $this->invalidateCache();
51
52
        return true;
53
    }
54
    
55
    /**
56
     * @inheritdoc
57
     */
58
    protected function updateItem($name, $item)
59
    {
60
        if ($item->name !== $name && !$this->supportsCascadeUpdate()) {
61
            $this->db->createCommand()
62
                ->update($this->itemChildTable, ['parent' => $item->name], ['parent' => $name])
63
                ->execute();
64
            $this->db->createCommand()
65
                ->update($this->itemChildTable, ['child' => $item->name], ['child' => $name])
66
                ->execute();
67
            $this->db->createCommand()
68
                ->update($this->assignmentTable, ['item_name' => $item->name], ['item_name' => $name])
69
                ->execute();
70
        }
71
72
        $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...
73
74
        $this->db->createCommand()
75
            ->update($this->itemTable, [
76
                'name' => $item->name,
77
                'description' => $item->description,
78
                'rule_name' => $item->ruleName,
79
                'data' => $item->data === null ? null : serialize($item->data),
80
                'updated_at' => $item->updatedAt,
81
            ], [
82
                'name' => $name,
83
            ])->execute();
84
85
        $this->invalidateCache();
86
87
        return true;
88
    }
89
90
    /**
91
     * @inheritdoc
92
     */
93
    protected function addRule($rule)
94
    {
95
        $time = date('Y-m-d H:i:s');
96
        if ($rule->createdAt === null) {
97
            $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...
98
        }
99
        if ($rule->updatedAt === null) {
100
            $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...
101
        }
102
        $this->db->createCommand()
103
            ->insert($this->ruleTable, [
104
                'name' => $rule->name,
105
                'data' => serialize($rule),
106
                'created_at' => $rule->createdAt,
107
                'updated_at' => $rule->updatedAt,
108
            ])->execute();
109
110
        $this->invalidateCache();
111
112
        return true;
113
    }
114
    
115
    /**
116
     * @inheritdoc
117
     */
118
    protected function updateRule($name, $rule)
119
    {
120
        if ($rule->name !== $name && !$this->supportsCascadeUpdate()) {
121
            $this->db->createCommand()
122
                ->update($this->itemTable, ['rule_name' => $rule->name], ['rule_name' => $name])
123
                ->execute();
124
        }
125
126
        $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...
127
128
        $this->db->createCommand()
129
            ->update($this->ruleTable, [
130
                'name' => $rule->name,
131
                'data' => serialize($rule),
132
                'updated_at' => $rule->updatedAt,
133
            ], [
134
                'name' => $name,
135
            ])->execute();
136
137
        $this->invalidateCache();
138
139
        return true;
140
    }
141
    
142
    /**
143
     * 
144
     * @param string|User $userGuid
145
     * @return array
146
     */
147 1
    public function getRolesByUser($userGuid) {
148 1
        if (!isset($userGuid) || $userGuid === '') {
149 1
            return [];
150
        }
151
        
152 1
        if ($userGuid instanceof User) {
153 1
            $userGuid = $userGuid->getGUID();
154
        }
155
156 1
        $query = (new Query)->select('b.*')
157 1
            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])
158 1
            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')
159 1
            ->andWhere(['a.user_guid' => (string) $userGuid])
160 1
            ->andWhere(['b.type' => Item::TYPE_ROLE]);
161
162 1
        $roles = [];
163 1
        foreach ($query->all($this->db) as $row) {
164 1
            $roles[$row['name']] = $this->populateItem($row);
165
        }
166 1
        return $roles;
167
    }
168
169
    /**
170
     * Returns all permissions that are directly assigned to user.
171
     * @param string|User $userGuid the user GUID (see [[\rhosocial\user\User::guid]])
172
     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.
173
     */
174 3
    protected function getDirectPermissionsByUser($userGuid)
175
    {
176 3
        $query = (new Query)->select('b.*')
177 3
            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])
178 3
            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')
179 3
            ->andWhere(['a.user_guid' => (string) $userGuid])
180 3
            ->andWhere(['b.type' => Item::TYPE_PERMISSION]);
181
182 3
        $permissions = [];
183 3
        foreach ($query->all($this->db) as $row) {
184
            $permissions[$row['name']] = $this->populateItem($row);
185
        }
186 3
        return $permissions;
187
    }
188
189
    /**
190
     * Returns all permissions that the user inherits from the roles assigned to him.
191
     * @param string|User $userGuid the user GUID (see [[\rhosocial\user\User::guid]])
192
     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.
193
     */
194 3
    protected function getInheritedPermissionsByUser($userGuid)
195
    {
196 3
        $query = (new Query)->select('item_name')
197 3
            ->from($this->assignmentTable)
198 3
            ->where(['user_guid' => (string) $userGuid]);
199
200 3
        $childrenList = $this->getChildrenList();
201 3
        $result = [];
202 3
        foreach ($query->column($this->db) as $roleName) {
203 1
            $this->getChildrenRecursive($roleName, $childrenList, $result);
204
        }
205
206 3
        if (empty($result)) {
207 2
            return [];
208
        }
209
210 1
        $query = (new Query)->from($this->itemTable)->where([
211 1
            'type' => Item::TYPE_PERMISSION,
212 1
            'name' => array_keys($result),
213
        ]);
214 1
        $permissions = [];
215 1
        foreach ($query->all($this->db) as $row) {
216 1
            $permissions[$row['name']] = $this->populateItem($row);
217
        }
218 1
        return $permissions;
219
    }
220
221
    /**
222
     * @inheritdoc
223
     */
224 10
    public function getAssignment($roleName, $userGuid)
225
    {
226 10
        if (empty($userGuid)) {
227 1
            return null;
228
        }
229
        
230 10
        if ($userGuid instanceof User) {
231 9
            $userGuid = $userGuid->getGUID();
232
        }
233
234 10
        $row = (new Query)->from($this->assignmentTable)
235 10
            ->where(['user_guid' => (string) $userGuid, 'item_name' => $roleName])
236 10
            ->one($this->db);
237
238 10
        if ($row === false) {
239 1
            return null;
240
        }
241
        
242 9
        $assignment = new Assignment([
243 9
            'userGuid' => $row['user_guid'],
244 9
            'roleName' => $row['item_name'],
245 9
            'createdAt' => $row['created_at'],
246 9
            'failedAt' => $row['failed_at'],
247
        ]);
248
        
249 9
        if ($this->revokeFailedAssignment($assignment)) {
250 2
            return null;
251
        }
252
253 7
        return $assignment;
254
    }
255
    
256
    /**
257
     * Revoke failed assignment.
258
     * If assignment's `failedAt` attribute is `null`, false will be given directly.
259
     * @param Assignment $assignment
260
     * @return boolean
261
     */
262 9
    protected function revokeFailedAssignment(Assignment $assignment)
263
    {
264 9
        if ($assignment->failedAt === null) {
265 7
            return false;
266
        }
267 2
        if (strtotime($assignment->failedAt) < strtotime(date('Y-m-d H:i:s'))) {
268 2
            $role = $this->getRole($assignment->roleName);
269 2
            if (!$role) {
270 1
                return true;
271
            }
272 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...
273 1
            return true;
274
        }
275
        return false;
276
    }
277
278
    /**
279
     * @inheritdoc
280
     */
281 7
    public function getAssignments($userGuid)
282
    {
283 7
        if (empty($userGuid)) {
284 1
            return [];
285
        }
286
        
287 7
        if ($userGuid instanceof User) {
288 1
            $userGuid = $userGuid->getGUID();
289
        }
290
291 7
        $query = (new Query)
292 7
            ->from($this->assignmentTable)
293 7
            ->where(['user_guid' => (string) $userGuid]);
294
295 7
        $assignments = [];
296 7
        foreach ($query->all($this->db) as $row) {
297 6
            $assignment = new Assignment([
298 6
                'userGuid' => $row['user_guid'],
299 6
                'roleName' => $row['item_name'],
300 6
                'createdAt' => $row['created_at'],
301 6
                'failedAt' => $row['failed_at'],
302
            ]);
303 6
            if ($this->revokeFailedAssignment($assignment)) {
304
                continue;
305
            }
306 6
            $assignments[$row['item_name']] = $assignment;
307
        }
308
309 7
        return $assignments;
310
    }
311
312
    /**
313
     * @inheritdoc
314
     */
315 14
    public function assign($role, $userGuid, $failedAt = null)
316
    {
317 14
        $assignment = new Assignment([
318 14
            'userGuid' => $userGuid,
319 14
            'roleName' => $role->name,
320 14
            'createdAt' => date('Y-m-d H:i:s'),
321 14
            'failedAt' => empty($failedAt) ? null : $failedAt,
322
        ]);
323
324 14
        $this->db->createCommand()
325 14
            ->insert($this->assignmentTable, [
326 14
                'user_guid' => $assignment->userGuid,
327 14
                'item_name' => $assignment->roleName,
328 14
                'created_at' => $assignment->createdAt,
329 14
                'failed_at' => $assignment->failedAt,
330 14
            ])->execute();
331
332 14
        return $assignment;
333
    }
334
335
    /**
336
     * @inheritdoc
337
     */
338 2
    public function revoke($role, $userGuid)
339
    {
340 2
        if (empty($userGuid)) {
341 1
            return false;
342
        }
343
        
344 2
        if ($userGuid instanceof User) {
345 1
            $userGuid = $userGuid->getGUID();
346
        }
347
348 2
        return $this->db->createCommand()
349 2
            ->delete($this->assignmentTable, ['user_guid' => (string) $userGuid, 'item_name' => $role->name])
350 2
            ->execute() > 0;
351
    }
352
353
    /**
354
     * @inheritdoc
355
     */
356 1
    public function revokeAll($userGuid)
357
    {
358 1
        if (empty($userGuid)) {
359 1
            return false;
360
        }
361
        
362 1
        if ($userGuid instanceof User) {
363 1
            $userGuid = $userGuid->getGUID();
364
        }
365
366 1
        return $this->db->createCommand()
367 1
            ->delete($this->assignmentTable, ['user_guid' => (string) $userGuid])
368 1
            ->execute() > 0;
369
    }
370
371
    /**
372
     * Returns all role assignment information for the specified role.
373
     * @param string $roleName
374
     * @return Assignment[] the assignments. An empty array will be
375
     * returned if role is not assigned to any user.
376
     * @since 2.0.7
377
     */
378 1
    public function getUserGuidsByRole($roleName)
379
    {
380 1
        if (empty($roleName)) {
381 1
            return [];
382
        }
383
384 1
        return (new Query)->select('[[user_guid]]')
385 1
            ->from($this->assignmentTable)
386 1
            ->where(['item_name' => $roleName])->column($this->db);
387
    }
388
389
    /**
390
     * Populates an auth item with the data fetched from database
391
     * @param array $row the data from the auth item table
392
     * @return Item the populated auth item instance (either Role or Permission)
393
     */
394 10
    protected function populateItem($row)
395
    {
396 10
        $class = $row['type'] == Item::TYPE_PERMISSION ? Permission::class : Role::class;
397
398 10
        if (!isset($row['data']) || ($data = @unserialize($row['data'])) === false) {
399 10
            $data = null;
400
        }
401
402 10
        return new $class([
403 10
            'name' => $row['name'],
404 10
            'type' => $row['type'],
405 10
            'description' => $row['description'],
406 10
            'ruleName' => $row['rule_name'],
407 10
            'data' => $data,
408 10
            'color' => $row['color'],
409 10
            'createdAt' => $row['created_at'],
410 10
            'updatedAt' => $row['updated_at'],
411
        ]);
412
    }
413
}