Completed
Push — devel ( 92ccdd...3d6262 )
by Philippe
02:02
created

Manager::getParentsGuid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4285
cc 2
eloc 7
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * Manager.php
4
 *
5
 * PHP version 5.6+
6
 *
7
 * @author Philippe Gaultier <[email protected]>
8
 * @copyright 2010-2016 Philippe Gaultier
9
 * @license http://www.sweelix.net/license license
10
 * @version XXX
11
 * @link http://www.sweelix.net
12
 * @package sweelix\rbac\redis
13
 */
14
15
namespace sweelix\rbac\redis;
16
17
use sweelix\guid\Guid;
18
use Yii;
19
use yii\base\InvalidCallException;
20
use yii\base\InvalidParamException;
21
use yii\di\Instance;
22
use yii\rbac\Assignment;
23
use yii\rbac\BaseManager;
24
use yii\rbac\Permission;
25
use yii\rbac\Role;
26
use yii\rbac\Item;
27
use yii\rbac\Rule;
28
use yii\redis\Connection;
29
30
/**
31
 * REDIS Manager represents an authorization manager that stores
32
 * authorization information in REDIS database
33
 *
34
 * @author Philippe Gaultier <[email protected]>
35
 * @copyright 2010-2016 Philippe Gaultier
36
 * @license http://www.sweelix.net/license license
37
 * @version XXX
38
 * @link http://www.sweelix.net
39
 * @package application\controllers
40
 * @since XXX
41
 */
42
class Manager extends BaseManager
43
{
44
    /**
45
     * @var Connection|array|string the Redis DB connection object or the application component ID of the DB connection.
46
     */
47
    public $db = 'redis';
48
49
    /**
50
     * @var string
51
     */
52
    public $globalMatchKey = 'auth:*';
53
54
    /**
55
     * @var string
56
     */
57
    public $userAssignmentsKey = 'auth:users:{id}:assignments';
58
59
    /**
60
     * @var string
61
     */
62
    public $roleAssignmentsKey = 'auth:roles:{id}:assignments';
63
64
    /**
65
     * @var string
66
     */
67
    public $ruleKey = 'auth:rules:{id}';
68
69
    /**
70
     * @var string
71
     */
72
    public $typeItemsKey = 'auth:types:{id}:items';
73
74
    /**
75
     * @var string
76
     */
77
    public $itemKey = 'auth:items:{id}';
78
79
    /**
80
     * @var string
81
     */
82
    public $ruleItemsKey = 'auth:rules:{id}:items';
83
84
    /**
85
     * @var string
86
     */
87
    public $itemChildrenKey = 'auth:items:{id}:children';
88
89
    /**
90
     * @var string
91
     */
92
    public $itemParentsKey = 'auth:items:{id}:parents';
93
94
    /**
95
     * @var string
96
     */
97
    public $itemMappings = 'auth:mappings:items';
98
99
    /**
100
     * @var string
101
     */
102
    public $itemMappingsGuid = 'auth:mappings:itemsguid';
103
104
    /**
105
     * @var string
106
     */
107
    public $ruleMappings = 'auth:mappings:rules';
108
109
    /**
110
     * @var string
111
     */
112
    public $ruleMappingsGuid = 'auth:mappings:rulesguid';
113
114
115
    /**
116
     * @param string|integer $userId user id
117
     * @return string the user assignments key
118
     * @since XXX
119
     */
120 19
    public function getUserAssignmentsKey($userId)
121
    {
122 19
        return str_replace('{id}', $userId, $this->userAssignmentsKey);
123
    }
124
125
    /**
126
     * @param string $roleGuid role guid
127
     * @return string the rule assignments key
128
     * @since XXX
129
     */
130 19
    public function getRoleAssignmentsKey($roleGuid)
131
    {
132 19
        return str_replace('{id}', $roleGuid, $this->roleAssignmentsKey);
133
    }
134
135
    /**
136
     * @param string $ruleGuid rule guid
137
     * @return string the rule key
138
     * @since XXX
139
     */
140 21
    public function getRuleKey($ruleGuid)
141
    {
142 21
        return str_replace('{id}', $ruleGuid, $this->ruleKey);
143
    }
144
145
    /**
146
     * @param integer $typeId type id
147
     * @return string the type id key
148
     * @since XXX
149
     */
150 28
    public function getTypeItemsKey($typeId)
151
    {
152 28
        return str_replace('{id}', $typeId, $this->typeItemsKey);
153
    }
154
155
    /**
156
     * @param string $itemGuid item guid
157
     * @return string
158
     * @since XXX
159
     */
160 28
    public function getItemKey($itemGuid)
161
    {
162 28
        return str_replace('{id}', $itemGuid, $this->itemKey);
163
    }
164
165
    /**
166
     * @param string $ruleGuid rule guid
167
     * @return string the rule items key
168
     * @since XXX
169
     */
170 19
    public function getRuleItemsKey($ruleGuid)
171
    {
172 19
        return str_replace('{id}', $ruleGuid, $this->ruleItemsKey);
173
    }
174
175
    /**
176
     * @param string $itemGuid item guid
177
     * @return string the item children key
178
     * @since XXX
179
     */
180 23
    public function getItemChildrenKey($itemGuid)
181
    {
182 23
        return str_replace('{id}', $itemGuid, $this->itemChildrenKey);
183
    }
184
185
    /**
186
     * @param string $itemGuid item guid
187
     * @return string the item parents key
188
     * @since XXX
189
     */
190 23
    public function getItemParentsKey($itemGuid)
191
    {
192 23
        return str_replace('{id}', $itemGuid, $this->itemParentsKey);
193
    }
194
195
    /**
196
     * @return string the item mapping key
197
     * @since XXX
198
     */
199 28
    public function getItemMappingKey()
200
    {
201 28
        return $this->itemMappings;
202
    }
203
204
    /**
205
     * @return string the rule mapping key
206
     * @since XXX
207
     */
208 21
    public function getRuleMappingKey()
209
    {
210 21
        return $this->ruleMappings;
211
    }
212
213
    /**
214
     * @return string the item mapping key
215
     * @since XXX
216
     */
217 28
    public function getItemMappingGuidKey()
218
    {
219 28
        return $this->itemMappingsGuid;
220
    }
221
222
    /**
223
     * @return string the rule mapping key
224
     * @since XXX
225
     */
226 21
    public function getRuleMappingGuidKey()
227
    {
228 21
        return $this->ruleMappingsGuid;
229
    }
230
231
232
    /**
233
     * @inheritdoc
234
     */
235 30
    public function init()
236
    {
237 30
        parent::init();
238 30
        $this->db = Instance::ensure($this->db, Connection::className());
239 30
    }
240
241
    /**
242
     * @inheritdoc
243
     */
244 10
    protected function getItem($name)
245
    {
246 10
        $item = null;
247 10
        $guid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $name]);
248 10
        if ($guid !== null)
249 10
        {
250 10
            $item = $this->getItemByGuid($guid, $name);
251 10
        }
252 10
        return $item;
253
    }
254
255
    /**
256
     * @param string $guid item guid
257
     * @param string $name name if known to avoid additional call
258
     * @return mixed
259
     * @since XXX
260
     */
261 24
    protected function getItemByGuid($guid, $name = null)
262
    {
263 24
        if ($name === null) {
264 23
            $name = $this->db->executeCommand('HGET', [$this->getItemMappingGuidKey(), $guid]);
265 23
        }
266 24
        $data = $this->db->executeCommand('HGETALL', [$this->getItemKey($guid)]);
267 24
        $dataRow = ['name' => $name];
268 24
        for ($i = 0; $i < count($data); $i = $i + 2) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
269 24
            $dataRow[$data[$i]] = $data[($i + 1)];
270 24
        }
271 24
        if (isset($dataRow['ruleGuid']) === true) {
272 19
            $ruleName = $this->db->executeCommand('HGET', [$this->getRuleMappingGuidKey(), $dataRow['ruleGuid']]);
273 19
            if ($ruleName !== null) {
274 19
                $dataRow['ruleName'] = $ruleName;
275 19
            }
276 19
            unset($dataRow['ruleGuid']);
277 24
        } elseif(isset($dataRow['ruleClass']) === true) {
278 1
            $dataRow['ruleName'] = $dataRow['ruleClass'];
279 1
            unset($dataRow['ruleClass']);
280 1
        }
281 24
        $item = $this->populateItem($dataRow);
282 24
        return $item;
283
    }
284
285
    /**
286
     * @inheritdoc
287
     */
288 3
    protected function getItems($type)
289
    {
290 3
        $itemGuids = $this->db->executeCommand('SMEMBERS', [$this->getTypeItemsKey($type)]);
291 3
        $items = [];
292 3
        foreach($itemGuids as $itemGuid) {
293 3
            $item = $this->getItemByGuid($itemGuid);
294 3
            $items[$item->name] = $item;
295 3
        }
296 3
        return $items;
297
    }
298
299
    /**
300
     * @inheritdoc
301
     */
302 28
    protected function addItem($item)
303
    {
304 28
        if (empty($item->name) === true) {
305 1
            throw new InvalidParamException("Item name must be defined");
306
        }
307 28
        $itemExists = (int)$this->db->executeCommand('HEXISTS', [$this->getItemMappingKey(), $item->name]);
308 28
        if ($itemExists === 1)
309 28
        {
310 1
            throw new DuplicateKeyException("Rule '{$item->name}' already defined");
311
        }
312 28
        $guid = Guid::v4();
313 28
        $time = time();
314 28
        if ($item->createdAt === null) {
315 28
            $item->createdAt = $time;
316 28
        }
317 28
        if ($item->updatedAt === null) {
318 28
            $item->updatedAt = $time;
319 28
        }
320 28
        $ruleGuid = null;
321 28
        $ruleClass = null;
322 28
        if (empty($item->ruleName) === false) {
323 19
            $ruleGuid = $this->db->executeCommand('HGET', [$this->getRuleMappingKey(), $item->ruleName]);
324 19
            if (($ruleGuid === null) && class_exists($item->ruleName)) {
325 1
                $ruleClass = $item->ruleName;
326 19
            } elseif(($ruleGuid === null) && (Yii::$container->has($item->ruleName))) {
327 1
                $ruleClass = $item->ruleName;
328 1
            }
329 19
        }
330
331 28
        $this->db->executeCommand('MULTI');
332
        // update mapping
333 28
        $this->db->executeCommand('HSET', [$this->getItemMappingKey(), $item->name, $guid]);
334 28
        $this->db->executeCommand('HSET', [$this->getItemMappingGuidKey(), $guid, $item->name]);
335 28
        $insertItem = [$this->getItemKey($guid),
336 28
            'data', serialize($item->data),
337 28
            'type', $item->type,
338 28
            'createdAt', $item->createdAt,
339 28
            'updatedAt', $item->updatedAt
340 28
        ];
341 28
        if ($item->description !== null) {
342 22
            $insertItem[] = 'description';
343 22
            $insertItem[] = $item->description;
344 22
        }
345 28
        if ($ruleGuid !== null) {
346 19
            $insertItem[] = 'ruleGuid';
347 19
            $insertItem[] = $ruleGuid;
348 19
        }
349 28
        if ($ruleClass !== null) {
350 1
            $insertItem[] = 'ruleClass';
351 1
            $insertItem[] = $ruleClass;
352 1
        }
353
354
        // insert item
355 28
        $this->db->executeCommand('HMSET', $insertItem);
356 28
        $this->db->executeCommand('SADD', [$this->getTypeItemsKey($item->type), $guid]);
357
        // affect rule
358 28
        if ($ruleGuid !== null) {
359 19
            $this->db->executeCommand('SADD', [$this->getRuleItemsKey($ruleGuid), $guid]);
360 19
        }
361 28
        $this->db->executeCommand('EXEC');
362 28
        return true;
363
364
    }
365
366
    /**
367
     * @inheritdoc
368
     */
369 2
    protected function updateItem($name, $item)
370
    {
371 2
        $item->updatedAt = time();
372
373 2
        $guid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $name]);
374 2
        $ruleGuid = null;
375 2
        $ruleClass = null;
376 2
        if (empty($item->ruleName) === false) {
377 1
            $ruleGuid = $this->db->executeCommand('HGET', [$this->getRuleMappingKey(), $item->ruleName]);
378 1
            if (($ruleGuid === null) && class_exists($item->ruleName)) {
379
                $ruleClass = $item->ruleName;
380 1
            } elseif(($ruleGuid === null) && (Yii::$container->has($item->ruleName))) {
381 1
                $ruleClass = $item->ruleName;
382 1
            }
383 1
        }
384 2
        $newRule = ($ruleGuid === null) ? $ruleClass : $ruleGuid;
385
386 2
        list($currentRuleGuid, $currentRuleClass, $currentType) = $this->db->executeCommand('HMGET', [$this->getItemKey($guid), 'ruleGuid', 'ruleClass', 'type']);
387
388 2
        $oldRule = ($currentRuleGuid === null) ? $currentRuleClass : $currentRuleGuid;
389
390 2
        $this->db->executeCommand('MULTI');
391 2
        if ($name !== $item->name) {
392
            // delete old mapping
393 2
            $this->db->executeCommand('HDEL', [$this->getItemMappingKey(), $name]);
394
            // add new mapping
395 2
            $this->db->executeCommand('HSET', [$this->getItemMappingKey(), $item->name, $guid]);
396 2
            $this->db->executeCommand('HSET', [$this->getItemMappingGuidKey(), $guid, $item->name]);
397 2
        }
398
399 2
        $updateEmptyItem = [$this->getItemKey($guid)];
400 2
        $updateItem = [$this->getItemKey($guid),
401 2
            'data', serialize($item->data),
402 2
            'type', $item->type,
403 2
            'createdAt', $item->createdAt,
404 2
            'updatedAt', $item->updatedAt
405 2
        ];
406 2
        if ($item->description !== null) {
407 1
            $updateItem[] = 'description';
408 1
            $updateItem[] = $item->description;
409 1
        } else {
410 1
            $updateEmptyItem[] = 'description';
411
        }
412 2
        if ($newRule !== $oldRule) {
413 1
            if ($currentRuleGuid !== null) {
414
                $this->db->executeCommand('SREM', [$this->getRuleItemsKey($currentRuleGuid), $guid]);
415
            }
416 1
            if ($ruleGuid !== null) {
417
                $this->db->executeCommand('SADD', [$this->getRuleItemsKey($ruleGuid), $guid]);
418
            }
419 1
            if ($ruleGuid !== null) {
420
                $updateItem[] = 'ruleGuid';
421
                $updateItem[] = $ruleGuid;
422
            } else {
423 1
                $updateEmptyItem[] = 'ruleGuid';
424
            }
425 1
            if ($ruleClass !== null) {
426 1
                $updateItem[] = 'ruleClass';
427 1
                $updateItem[] = $ruleClass;
428 1
            } else {
429
                $updateEmptyItem[] = 'ruleClass';
430
            }
431 1
        }
432 2
        if ($item->type !== $currentType) {
433
            $this->db->executeCommand('SREM', [$this->getTypeItemsKey($currentType), $guid]);
434
            $this->db->executeCommand('SADD', [$this->getTypeItemsKey($item->type), $guid]);
435
        }
436
437
        // update item
438 2
        $this->db->executeCommand('HMSET', $updateItem);
439
        // remove useless props
440 2
        if (count($updateEmptyItem) > 1) {
441 1
            $this->db->executeCommand('HDEL', $updateEmptyItem);
442 1
        }
443
444
445 2
        $this->db->executeCommand('EXEC');
446 2
        return true;
447
448
    }
449
450
    /**
451
     * @inheritdoc
452
     */
453 3
    protected function removeItem($item)
454
    {
455 3
        $guid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $item->name]);
456 3
        $ruleGuid = $this->db->executeCommand('HGET', [$this->getRuleMappingKey(), $item->ruleName]);
457
458 3
        $parentGuids = $this->db->executeCommand('SMEMBERS', [$this->getItemParentsKey($guid)]);
459 3
        $childrenGuids = $this->db->executeCommand('SMEMBERS', [$this->getItemChildrenKey($guid)]);
460
461 3
        $this->db->executeCommand('MULTI');
462
        // delete mapping
463 3
        $this->db->executeCommand('HDEL', [$this->getItemMappingKey(), $item->name]);
464 3
        $this->db->executeCommand('HDEL', [$this->getItemMappingGuidKey(), $guid]);
465
        // delete rule <-> item link
466 3
        $this->db->executeCommand('SREM', [$this->getRuleItemsKey($ruleGuid), $guid]);
467 3
        $this->db->executeCommand('SREM', [$this->getTypeItemsKey($item->type), $guid]);
468
        // detach from hierarchy
469 3
        foreach($parentGuids as $parentGuid) {
470 2
            $this->db->executeCommand('SREM', [$this->getItemChildrenKey($parentGuid), $guid]);
471 3
        }
472
        // detach children
473 3
        foreach($childrenGuids as $childGuid) {
474 1
            $this->db->executeCommand('SREM', [$this->getItemParentsKey($childGuid), $guid]);
475 3
        }
476 3
        $this->db->executeCommand('DEL', [$this->getItemParentsKey($guid)]);
477 3
        $this->db->executeCommand('DEL', [$this->getItemChildrenKey($guid)]);
478
        // delete rule
479 3
        $this->db->executeCommand('DEL', [$this->getItemKey($guid)]);
480 3
        $this->db->executeCommand('EXEC');
481 3
        return true;
482
483
    }
484
485
    /**
486
     * @inheritdoc
487
     */
488 21
    protected function addRule($rule)
489
    {
490 21
        if(empty($rule->name) === true) {
491 1
            throw new InvalidParamException("Rule name must be defined");
492
        }
493 21
        $ruleExists = (int)$this->db->executeCommand('HEXISTS', [$this->getRuleMappingKey(), $rule->name]);
494 21
        if ($ruleExists === 1)
495 21
        {
496 1
            throw new DuplicateKeyException("Rule '{$rule->name}' already defined");
497
        }
498 21
        $guid = Guid::v4();
499 21
        $time = time();
500 21
        if ($rule->createdAt === null) {
501 21
            $rule->createdAt = $time;
502 21
        }
503 21
        if ($rule->updatedAt === null) {
504 21
            $rule->updatedAt = $time;
505 21
        }
506 21
        $this->db->executeCommand('MULTI');
507 21
        $this->db->executeCommand('HSET', [$this->getRuleMappingKey(), $rule->name, $guid]);
508 21
        $this->db->executeCommand('HSET', [$this->getRuleMappingGuidKey(),$guid, $rule->name]);
509 21
        $this->db->executeCommand('HMSET', [$this->getRuleKey($guid),
510 21
            'data', serialize($rule),
511 21
            'createdAt', $rule->createdAt,
512 21
            'updatedAt', $rule->updatedAt
513 21
        ]);
514
515 21
        $this->db->executeCommand('EXEC');
516 21
        return true;
517
518
    }
519
520
    /**
521
     * @inheritdoc
522
     */
523 1
    protected function updateRule($name, $rule)
524
    {
525 1
        $rule->updatedAt = time();
526
527 1
        $guid = $this->db->executeCommand('HGET', [$this->getRuleMappingKey(), $name]);
528
529 1
        $this->db->executeCommand('MULTI');
530 1
        if ($name !== $rule->name) {
531
            // delete old mapping
532 1
            $this->db->executeCommand('HDEL', [$this->getRuleMappingKey(), $name]);
533
            // add new mapping
534 1
            $this->db->executeCommand('HSET', [$this->getRuleMappingKey(), $rule->name, $guid]);
535 1
            $this->db->executeCommand('HSET', [$this->getRuleMappingGuidKey(),$guid, $rule->name]);
536 1
        }
537 1
        $this->db->executeCommand('HMSET', [$this->getRuleKey($guid),
538 1
            'data', serialize($rule),
539 1
            'createdAt', $rule->createdAt,
540 1
            'updatedAt', $rule->updatedAt
541 1
        ]);
542
543 1
        $this->db->executeCommand('EXEC');
544 1
        return true;
545
    }
546
547
    /**
548
     * @inheritdoc
549
     */
550 2
    protected function removeRule($rule)
551
    {
552 2
        $guid = $this->db->executeCommand('HGET', [$this->getRuleMappingKey(), $rule->name]);
553
554 2
        $ruleMembers = $this->db->executeCommand('SMEMBERS', [$this->getRuleItemsKey($guid)]);
555
556 2
        $this->db->executeCommand('MULTI');
557
        // delete mapping
558 2
        $this->db->executeCommand('HDEL', [$this->getRuleMappingKey(), $rule->name]);
559 2
        $this->db->executeCommand('HDEL', [$this->getRuleMappingGuidKey(), $guid]);
560
        // detach items
561 2
        foreach($ruleMembers as $itemGuid)
562
        {
563 2
            $this->db->executeCommand('HDEL', [$this->getItemKey($itemGuid), 'ruleGuid']);
564 2
        }
565
        // delete rule <-> item link
566 2
        $this->db->executeCommand('DEL', [$this->getRuleItemsKey($guid)]);
567
        // delete rule
568 2
        $this->db->executeCommand('DEL', [$this->getRuleKey($guid)]);
569 2
        $this->db->executeCommand('EXEC');
570 2
        return true;
571
    }
572
573
    /**
574
     * @inheritdoc
575
     */
576 5
    public function getRules()
577
    {
578 5
        $ruleNames = $this->db->executeCommand('HKEYS', [$this->getRuleMappingKey()]);
579 5
        $rules = [];
580 5
        foreach ($ruleNames as $ruleName)
581
        {
582 4
            $rules[$ruleName] = $this->getRule($ruleName);
583 5
        }
584 5
        return $rules;
585
    }
586
587
    /**
588
     * @inheritdoc
589
     */
590 10
    public function getRule($name)
591
    {
592 10
        $rule = null;
593 10
        $guid = $this->db->executeCommand('HGET', [$this->getRuleMappingKey(), $name]);
594 10
        if ($guid !== null) {
595 10
            $rule = $this->getRuleGuid($guid);
596 10
        } elseif(class_exists($name) === true) {
597 1
            $rule = new $name;
598 3
        } elseif(Yii::$container->has($name) === true) {
599 1
            $rule = Yii::$container->get($name);
600 1
        }
601 10
        return $rule;
602
    }
603
604
    /**
605
     * @param string $guid rule unique ID
606
     * @return Rule
607
     * @since XXX
608
     */
609 10
    protected function getRuleGuid($guid)
610
    {
611 10
        $data = $this->db->executeCommand('HGET', [$this->getRuleKey($guid), 'data']);
612 10
        $rule = unserialize($data);
613 10
        return $rule;
614
    }
615
616
    /**
617
     * @inheritdoc
618
     */
619 23
    public function addChild($parent, $child)
620
    {
621 23
        if ($parent->name === $child->name) {
622 1
            throw new InvalidParamException("Cannot add '{$parent->name}' as a child of itself.");
623
        }
624 23
        if ($parent instanceof Permission && $child instanceof Role) {
625 1
            throw new InvalidParamException('Cannot add a role as a child of a permission.');
626
        }
627 23
        if ($this->detectLoop($parent, $child)) {
628 1
            throw new InvalidCallException("Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.");
629
        }
630
631 23
        list($parentGuid, $childGuid) = $this->db->executeCommand('HMGET', [$this->getItemMappingKey(), $parent->name, $child->name]);
632
633 23
        $this->db->executeCommand('MULTI');
634 23
        $this->db->executeCommand('SADD', [$this->getItemParentsKey($childGuid), $parentGuid]);
635 23
        $this->db->executeCommand('SADD', [$this->getItemChildrenKey($parentGuid), $childGuid]);
636 23
        $this->db->executeCommand('EXEC');
637 23
        return true;
638
    }
639
640
    /**
641
     * @inheritdoc
642
     */
643 1
    public function removeChild($parent, $child)
644
    {
645 1
        list($parentGuid, $childGuid) = $this->db->executeCommand('HMGET', [$this->getItemMappingKey(), $parent->name, $child->name]);
646 1
        $this->db->executeCommand('MULTI');
647 1
        $this->db->executeCommand('SREM', [$this->getItemParentsKey($childGuid), $parentGuid]);
648 1
        $this->db->executeCommand('SREM', [$this->getItemChildrenKey($parentGuid), $childGuid]);
649 1
        $this->db->executeCommand('EXEC');
650 1
        return true;
651
652
    }
653
654
    /**
655
     * @inheritdoc
656
     */
657 1
    public function removeChildren($parent)
658
    {
659 1
        $guid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $parent->name]);
660 1
        $childrenGuids = $this->db->executeCommand('SMEMBERS', [$this->getItemChildrenKey($guid)]);
661
662 1
        $this->db->executeCommand('MULTI');
663 1
        foreach($childrenGuids as $childGuid)
664
        {
665 1
            $this->db->executeCommand('SREM', [$this->getItemParentsKey($childGuid), $guid]);
666 1
        }
667 1
        $this->db->executeCommand('DEL', [$this->getItemChildrenKey($guid)]);
668 1
        $this->db->executeCommand('EXEC');
669 1
        return true;
670
671
    }
672
673
    /**
674
     * @inheritdoc
675
     */
676 23
    public function getChildren($name)
677
    {
678 23
        $guid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $name]);
679 23
        return $this->getChildrenByGuid($guid);
680
681
    }
682
683
    /**
684
     * @param string $guid item guid
685
     * @return Item[]
686
     * @since XXX
687
     */
688 23
    protected function getChildrenByGuid($guid)
689
    {
690 23
        $childrenGuids = $this->db->executeCommand('SMEMBERS', [$this->getItemChildrenKey($guid)]);
691 23
        $children = [];
692 23
        if (count($childrenGuids) > 0) {
693 23
            foreach($childrenGuids as $childGuid) {
694 23
                $children[] = $this->getItemByGuid($childGuid);
695 23
            }
696 23
        }
697 23
        return $children;
698
    }
699
700
    /**
701
     * @inheritdoc
702
     */
703 5
    public function hasChild($parent, $child)
704
    {
705 5
        list($parentGuid, $childGuid) = $this->db->executeCommand('HMGET', [$this->getItemMappingKey(), $parent->name, $child->name]);
706 5
        $result = (int)$this->db->executeCommand('SISMEMBER', [$this->getItemChildrenKey($parentGuid), $childGuid]);
707
708 5
        return $result === 1;
709
710
    }
711
712
    /**
713
     * @inheritdoc
714
     */
715 1
    public function revokeAll($userId)
716
    {
717 1
        if (empty($userId) === true) {
718 1
            return false;
719
        }
720 1
        $roleGuids = $this->db->executeCommand('ZRANGEBYSCORE', [$this->getUserAssignmentsKey($userId), '-inf', '+inf']);
721 1
        $this->db->executeCommand('MULTI');
722 1
        if (count($roleGuids) > 0) {
723 1
            foreach ($roleGuids as $roleGuid) {
724 1
                $this->db->executeCommand('ZREM', [$this->getRoleAssignmentsKey($roleGuid), $userId]);
725 1
            }
726 1
        }
727 1
        $this->db->executeCommand('DEL', [$this->getUserAssignmentsKey($userId)]);
728 1
        $this->db->executeCommand('EXEC');
729 1
        return true;
730
731
    }
732
733
    /**
734
     * @inheritdoc
735
     */
736 1
    public function revoke($role, $userId)
737
    {
738 1
        if (empty($userId) === true) {
739 1
            return false;
740
        }
741 1
        $roleGuid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $role->name]);
742
743 1
        $this->db->executeCommand('MULTI');
744 1
        $this->db->executeCommand('ZREM', [$this->getUserAssignmentsKey($userId), $roleGuid]);
745 1
        $this->db->executeCommand('ZREM', [$this->getRoleAssignmentsKey($roleGuid), $userId]);
746 1
        $this->db->executeCommand('EXEC');
747 1
        return true;
748
    }
749
750
    /**
751
     * @inheritdoc
752
     */
753 1
    public function removeAllRoles()
754
    {
755 1
        $this->removeAllItems(Item::TYPE_ROLE);
756 1
    }
757
758
    /**
759
     * @inheritdoc
760
     */
761 1
    public function removeAllRules()
762
    {
763 1
        $rules = $this->getRules();
764 1
        foreach($rules as $rule) {
765 1
            $this->removeRule($rule);
766 1
        }
767 1
    }
768
769
    /**
770
     * @inheritdoc
771
     */
772 1
    public function removeAllPermissions()
773
    {
774 1
        $this->removeAllItems(Item::TYPE_PERMISSION);
775 1
    }
776
777
    /**
778
     * @inheritdoc
779
     */
780 30
    public function removeAll()
781
    {
782 30
        $authKeys = [];
783 30
        $nextCursor = 0;
784
        do {
785 30
            list($nextCursor, $keys) = $this->db->executeCommand('SCAN', [$nextCursor, 'MATCH', $this->globalMatchKey]);
786 30
            $authKeys = array_merge($authKeys, $keys);
787
788 30
        } while($nextCursor != 0);
789
790 30
        if (count($authKeys) > 0) {
791 28
            $this->db->executeCommand('DEL', $authKeys);
792 28
        }
793 30
    }
794
795
    /**
796
     * @inheritdoc
797
     */
798 1
    public function removeAllAssignments()
799
    {
800 1
        $roleAssignKey = $this->getRoleAssignmentsKey('*');
801 1
        $userAssignKey = $this->getUserAssignmentsKey('*');
802 1
        $assignmentKeys = [];
803
804 1
        $nextCursor = 0;
805
        do {
806 1
            list($nextCursor, $keys) = $this->db->executeCommand('SCAN', [$nextCursor, 'MATCH', $roleAssignKey]);
807 1
            $assignmentKeys = array_merge($assignmentKeys, $keys);
808
809 1
        } while($nextCursor != 0);
810
811 1
        $nextCursor = 0;
812
        do {
813 1
            list($nextCursor, $keys) = $this->db->executeCommand('SCAN', [$nextCursor, 'MATCH', $userAssignKey]);
814 1
            $assignmentKeys = array_merge($assignmentKeys, $keys);
815
816 1
        } while($nextCursor != 0);
817
818 1
        if (count($assignmentKeys) > 0) {
819 1
            $this->db->executeCommand('DEL', $assignmentKeys);
820 1
        }
821 1
    }
822
823
    /**
824
     * @param integer $type
825
     * @since XXX
826
     */
827 2
    public function removeAllItems($type)
828
    {
829 2
        $items = $this->getItems($type);
830 2
        foreach ($items as $item) {
831 2
            $this->removeItem($item);
832 2
        }
833 2
    }
834
835
    /**
836
     * @inheritdoc
837
     */
838 4
    public function getRolesByUser($userId)
839
    {
840 4
        if (!isset($userId) || $userId === '') {
841 1
            return [];
842
        }
843 4
        $roleGuids = $this->db->executeCommand('ZRANGEBYSCORE', [$this->getUserAssignmentsKey($userId), '-inf', '+inf']);
844 4
        $roles = [];
845 4
        if (count($roleGuids) > 0) {
846 4
            foreach ($roleGuids as $roleGuid) {
847 4
                $isRole = (int)$this->db->executeCommand('SISMEMBER', [$this->getTypeItemsKey(Item::TYPE_ROLE), $roleGuid]);
848 4
                if ($isRole === 1) {
849 4
                    $item = $this->getItemByGuid($roleGuid);
850 4
                    $roles[$item->name] = $item;
851 4
                }
852 4
            }
853 4
        }
854
855 4
        return $roles;
856
    }
857
858
    /**
859
     * @inheritdoc
860
     */
861 2
    public function getUserIdsByRole($roleName)
862
    {
863 2
        if (empty($roleName)) {
864 1
            return [];
865
        }
866 1
        $roleGuid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $roleName]);
867 1
        $userIds = [];
868 1
        if ($roleGuid !== null) {
869 1
            $userIds = $this->db->executeCommand('ZRANGEBYSCORE', [$this->getRoleAssignmentsKey($roleGuid), '-inf', '+inf']);
870 1
        }
871 1
        return $userIds;
872
873
    }
874
875
    /**
876
     * @inheritdoc
877
     */
878 19
    public function assign($role, $userId)
879
    {
880 19
        $assignment = new Assignment([
881 19
            'userId' => $userId,
882 19
            'roleName' => $role->name,
883 19
            'createdAt' => time(),
884 19
        ]);
885 19
        $roleGuid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $role->name]);
886
887 19
        $this->db->executeCommand('MULTI');
888 19
        $this->db->executeCommand('ZADD', [$this->getUserAssignmentsKey($userId), $assignment->createdAt, $roleGuid]);
889 19
        $this->db->executeCommand('ZADD', [$this->getRoleAssignmentsKey($roleGuid), $assignment->createdAt, $userId]);
890 19
        $this->db->executeCommand('EXEC');
891 19
        return $assignment;
892
    }
893
894
    /**
895
     * @inheritdoc
896
     */
897 1
    public function getPermissionsByUser($userId)
898
    {
899 1
        $rolesGuids = $this->db->executeCommand('ZRANGEBYSCORE', [$this->getUserAssignmentsKey($userId), '-inf', '+inf']);
900 1
        $permissions = [];
901 1
        if (count($rolesGuids) > 0) {
902 1
            $permissionsGuid = [];
903 1
            foreach($rolesGuids as $roleGuid) {
904 1
                $isPerm = (int)$this->db->executeCommand('SISMEMBER', [$this->getTypeItemsKey(Item::TYPE_PERMISSION), $roleGuid]);
905 1
                if ($isPerm === 1) {
906 1
                    $permissionsGuid[] = $roleGuid;
907 1
                }
908 1
            }
909 1
            foreach ($rolesGuids as $roleGuid) {
910 1
                list(, $permGuids) = $this->getChildrenRecursiveGuid($roleGuid, Item::TYPE_PERMISSION);
911 1
                $permissionsGuid = array_merge($permissionsGuid, $permGuids);
912 1
            }
913 1
            foreach($permissionsGuid as $permissionGuid) {
914 1
                $item = $this->getItemByGuid($permissionGuid);
915 1
                $permissions[$item->name] = $item;
916 1
            }
917 1
        }
918 1
        return $permissions;
919
    }
920
921
    /**
922
     * @inheritdoc
923
     */
924 1
    public function getPermissionsByRole($roleName)
925
    {
926 1
        $roleGuid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $roleName]);
927 1
        $permissions = [];
928 1
        list(, $permissionsGuid) = $this->getChildrenRecursiveGuid($roleGuid, Item::TYPE_PERMISSION);
929 1
        foreach($permissionsGuid as $permissionGuid) {
930 1
            $item = $this->getItemByGuid($permissionGuid);
931 1
            $permissions[$item->name] = $item;
932 1
        }
933 1
        return $permissions;
934
    }
935
936
    /**
937
     * @param string $itemGuid item unique ID
938
     * @param integer $type Item type
939
     * @return array
940
     * @since XXX
941
     */
942 2
    protected function getChildrenRecursiveGuid($itemGuid, $type)
943
    {
944 2
        $childrenGuid = $this->db->executeCommand('SMEMBERS', [$this->getItemChildrenKey($itemGuid)]);
945 2
        $typedChildrenGuid = $this->db->executeCommand('SINTER', [$this->getItemChildrenKey($itemGuid), $this->getTypeItemsKey($type)]);
946 2
        foreach($childrenGuid as $childGuid) {
947 2
            list($subChildrenGuid, $subTypedChildrenGuid) = $this->getChildrenRecursiveGuid($childGuid, $type);
948 2
            $childrenGuid = array_merge($childrenGuid, $subChildrenGuid);
949 2
            $typedChildrenGuid = array_merge($typedChildrenGuid, $subTypedChildrenGuid);
950 2
        }
951 2
        return [$childrenGuid, $typedChildrenGuid];
952
    }
953
954
    /**
955
     * @param Item $parent parent item
956
     * @param Item $child child item
957
     * @return bool
958
     * @since XXX
959
     */
960 23
    protected function detectLoop(Item $parent, Item $child)
961
    {
962 23
        if ($child->name === $parent->name) {
963 2
            return true;
964
        }
965 23
        foreach ($this->getChildren($child->name) as $grandchild) {
966 19
            if ($this->detectLoop($parent, $grandchild)) {
967 2
                return true;
968
            }
969 23
        }
970 23
        return false;
971
    }
972
973
    /**
974
     * @param string $itemName item name
975
     * @return array
976
     * @since XXX
977
     */
978 1
    public function getParents($itemName)
979
    {
980 1
        $itemGuid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $itemName]);
981 1
        return $this->getParentsGuid($itemGuid);
982
    }
983
984 1
    protected function getParentsGuid($itemGuid)
985
    {
986 1
        $parentsGuid = $this->db->executeCommand('SMEMBERS', [$this->getItemParentsKey($itemGuid)]);
987 1
        $parents = [];
988 1
        if (count($parentsGuid) > 0) {
989 1
            array_unshift($parentsGuid, $this->getItemMappingGuidKey());
990 1
            $parents = $this->db->executeCommand('HMGET', $parentsGuid);
991 1
        }
992 1
        return $parents;
993
    }
994
995
    /**
996
     * @inheritdoc
997
     */
998 3
    public function getAssignments($userId)
999
    {
1000 3
        $roleGuids = $this->db->executeCommand('ZRANGEBYSCORE', [$this->getUserAssignmentsKey($userId), '-inf', '+inf', 'WITHSCORES']);
1001 3
        $assignments = [];
1002 3
        if (count($roleGuids) > 0) {
1003 3
            $guids = [];
1004 3
            $dates = [];
1005 3
            for($i=0; $i < count($roleGuids); $i = $i+2) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1006 3
                $guids[] = $roleGuids[$i];
1007 3
                $dates[] = $roleGuids[($i + 1)];
1008 3
            }
1009 3
            array_unshift($guids, $this->getItemMappingGuidKey());
1010 3
            $names = $this->db->executeCommand('HMGET', $guids);
1011 3
            foreach ($names as $i => $name) {
1012 3
                $assignments[$name] = new Assignment([
1013 3
                    'userId' => $userId,
1014 3
                    'roleName' => $name,
1015 3
                    'createdAt' => $dates[$i],
1016 3
                ]);
1017 3
            }
1018 3
        }
1019
1020 3
        return $assignments;
1021
1022
    }
1023
1024
    /**
1025
     * @inheritdoc
1026
     */
1027
    public function getAssignment($roleName, $userId)
1028
    {
1029
        $roleGuid = $this->db->executeCommand('HGET', [$this->getItemMappingKey(), $roleName]);
1030
        $assignment = null;
1031
        if ($roleGuid !== null) {
1032
            $assignmentScore = $this->db->executeCommand('ZSCORE', [$this->getUserAssignmentsKey($userId), $roleGuid]);
1033
            if ($assignmentScore !== null) {
1034
                $assignment = new Assignment([
1035
                    'userId' => $userId,
1036
                    'roleName' => $roleName,
1037
                    'createdAt' => $assignmentScore,
1038
                ]);
1039
            }
1040
        }
1041
        return $assignment;
1042
    }
1043
1044 2
    public function checkAccess($userId, $permissionName, $params = [])
1045
    {
1046 2
        $assignments = $this->getAssignments($userId);
1047 2
        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);
1048
    }
1049
1050
1051
    /**
1052
     * @param Item $parent parent item
1053
     * @param Item $child child item
1054
     * @return bool
1055
     * @since XXX
1056
     */
1057 1
    public function canAddChild(Item $parent, Item $child)
1058
    {
1059 1
        return !$this->detectLoop($parent, $child);
1060
    }
1061
1062 2
    protected function checkAccessRecursive($user, $itemName, $params, $assignments)
1063
    {
1064 2
        if (($item = $this->getItem($itemName)) === null) {
1065 1
            return false;
1066
        }
1067 2
        Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission: $itemName", __METHOD__);
1068 2
        if (!$this->executeRule($user, $item, $params)) {
1069 2
            return false;
1070
        }
1071 2
        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {
1072 2
            return true;
1073
        }
1074 1
        $parents = $this->getParents($itemName);
1075 1
        foreach ($parents as $parent) {
1076 1
            if ($this->checkAccessRecursive($user, $parent, $params, $assignments)) {
1077 1
                return true;
1078
            }
1079 1
        }
1080 1
        return false;
1081
    }
1082
1083
    /**
1084
     * @param array $dataRow
1085
     * @return Permission|Role
1086
     * @since XXX
1087
     */
1088 24
    protected function populateItem($dataRow)
1089
    {
1090 24
        $class = $dataRow['type'] == Item::TYPE_PERMISSION ? Permission::className() : Role::className();
1091 24
        if (!isset($dataRow['data']) || ($data = @unserialize($dataRow['data'])) === false) {
1092
            $data = null;
1093
        }
1094
1095 24
        return new $class([
1096 24
            'name' => $dataRow['name'],
1097 24
            'type' => $dataRow['type'],
1098 24
            'description' => isset($dataRow['description']) ? $dataRow['description'] : null,
1099 24
            'ruleName' => isset($dataRow['ruleName']) ? $dataRow['ruleName'] : null,
1100 24
            'data' => $data,
1101 24
            'createdAt' => $dataRow['createdAt'],
1102 24
            'updatedAt' => $dataRow['updatedAt'],
1103 24
        ]);
1104
    }
1105
}
1106