Completed
Push — php7-travis-apcu ( 9bbcee...fd63c3 )
by Alexander
14:47
created

PhpManager::invalidateScriptCache()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 5
nc 4
nop 1
crap 3.0416
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\rbac;
9
10
use Yii;
11
use yii\base\InvalidCallException;
12
use yii\base\InvalidArgumentException;
13
use yii\helpers\VarDumper;
14
15
/**
16
 * PhpManager represents an authorization manager that stores authorization
17
 * information in terms of a PHP script file.
18
 *
19
 * The authorization data will be saved to and loaded from three files
20
 * specified by [[itemFile]], [[assignmentFile]] and [[ruleFile]].
21
 *
22
 * PhpManager is mainly suitable for authorization data that is not too big
23
 * (for example, the authorization data for a personal blog system).
24
 * Use [[DbManager]] for more complex authorization data.
25
 *
26
 * Note that PhpManager is not compatible with facebooks [HHVM](http://hhvm.com/) because
27
 * it relies on writing php files and including them afterwards which is not supported by HHVM.
28
 *
29
 * For more details and usage information on PhpManager, see the [guide article on security authorization](guide:security-authorization).
30
 *
31
 * @author Qiang Xue <[email protected]>
32
 * @author Alexander Kochetov <[email protected]>
33
 * @author Christophe Boulain <[email protected]>
34
 * @author Alexander Makarov <[email protected]>
35
 * @since 2.0
36
 */
37
class PhpManager extends BaseManager
38
{
39
    /**
40
     * @var string the path of the PHP script that contains the authorization items.
41
     * This can be either a file path or a [path alias](guide:concept-aliases) to the file.
42
     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
43
     * @see loadFromFile()
44
     * @see saveToFile()
45
     */
46
    public $itemFile = '@app/rbac/items.php';
47
    /**
48
     * @var string the path of the PHP script that contains the authorization assignments.
49
     * This can be either a file path or a [path alias](guide:concept-aliases) to the file.
50
     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
51
     * @see loadFromFile()
52
     * @see saveToFile()
53
     */
54
    public $assignmentFile = '@app/rbac/assignments.php';
55
    /**
56
     * @var string the path of the PHP script that contains the authorization rules.
57
     * This can be either a file path or a [path alias](guide:concept-aliases) to the file.
58
     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
59
     * @see loadFromFile()
60
     * @see saveToFile()
61
     */
62
    public $ruleFile = '@app/rbac/rules.php';
63
64
    /**
65
     * @var Item[]
66
     */
67
    protected $items = []; // itemName => item
68
    /**
69
     * @var array
70
     */
71
    protected $children = []; // itemName, childName => child
72
    /**
73
     * @var array
74
     */
75
    protected $assignments = []; // userId, itemName => assignment
76
    /**
77
     * @var Rule[]
78
     */
79
    protected $rules = []; // ruleName => rule
80
81
82
    /**
83
     * Initializes the application component.
84
     * This method overrides parent implementation by loading the authorization data
85
     * from PHP script.
86
     */
87 50
    public function init()
88
    {
89 50
        parent::init();
90 50
        $this->itemFile = Yii::getAlias($this->itemFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($this->itemFile) can also be of type boolean. However, the property $itemFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
91 50
        $this->assignmentFile = Yii::getAlias($this->assignmentFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($this->assignmentFile) can also be of type boolean. However, the property $assignmentFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
92 50
        $this->ruleFile = Yii::getAlias($this->ruleFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($this->ruleFile) can also be of type boolean. However, the property $ruleFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
93 50
        $this->load();
94 50
    }
95
96
    /**
97
     * @inheritdoc
98
     */
99 22
    public function checkAccess($userId, $permissionName, $params = [])
100
    {
101 22
        $assignments = $this->getAssignments($userId);
102
103 22
        if ($this->hasNoAssignments($assignments)) {
104 8
            return false;
105
        }
106
107 14
        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);
108
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113 26
    public function getAssignments($userId)
114
    {
115 26
        return isset($this->assignments[$userId]) ? $this->assignments[$userId] : [];
116
    }
117
118
    /**
119
     * Performs access check for the specified user.
120
     * This method is internally called by [[checkAccess()]].
121
     *
122
     * @param string|int $user the user ID. This should can be either an integer or a string representing
123
     * the unique identifier of a user. See [[\yii\web\User::id]].
124
     * @param string $itemName the name of the operation that need access check
125
     * @param array $params name-value pairs that would be passed to rules associated
126
     * with the tasks and roles assigned to the user. A param with name 'user' is added to this array,
127
     * which holds the value of `$userId`.
128
     * @param Assignment[] $assignments the assignments to the specified user
129
     * @return bool whether the operations can be performed by the user.
130
     */
131 14
    protected function checkAccessRecursive($user, $itemName, $params, $assignments)
132
    {
133 14
        if (!isset($this->items[$itemName])) {
134 1
            return false;
135
        }
136
137
        /* @var $item Item */
138 14
        $item = $this->items[$itemName];
139 14
        Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);
140
141 14
        if (!$this->executeRule($user, $item, $params)) {
142 6
            return false;
143
        }
144
145 14
        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {
146 12
            return true;
147
        }
148
149 13
        foreach ($this->children as $parentName => $children) {
150 13
            if (isset($children[$itemName]) && $this->checkAccessRecursive($user, $parentName, $params, $assignments)) {
151 13
                return true;
152
            }
153
        }
154
155 3
        return false;
156
    }
157
158
    /**
159
     * @inheritdoc
160
     * @since 2.0.8
161
     */
162 1
    public function canAddChild($parent, $child)
163
    {
164 1
        return !$this->detectLoop($parent, $child);
165
    }
166
167
    /**
168
     * @inheritdoc
169
     */
170 44
    public function addChild($parent, $child)
171
    {
172 44
        if (!isset($this->items[$parent->name], $this->items[$child->name])) {
173
            throw new InvalidArgumentException("Either '{$parent->name}' or '{$child->name}' does not exist.");
174
        }
175
176 44
        if ($parent->name === $child->name) {
177
            throw new InvalidArgumentException("Cannot add '{$parent->name} ' as a child of itself.");
178
        }
179 44
        if ($parent instanceof Permission && $child instanceof Role) {
180
            throw new InvalidArgumentException('Cannot add a role as a child of a permission.');
181
        }
182
183 44
        if ($this->detectLoop($parent, $child)) {
184
            throw new InvalidCallException("Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.");
185
        }
186 44
        if (isset($this->children[$parent->name][$child->name])) {
187
            throw new InvalidCallException("The item '{$parent->name}' already has a child '{$child->name}'.");
188
        }
189 44
        $this->children[$parent->name][$child->name] = $this->items[$child->name];
190 44
        $this->saveItems();
191
192 44
        return true;
193
    }
194
195
    /**
196
     * Checks whether there is a loop in the authorization item hierarchy.
197
     *
198
     * @param Item $parent parent item
199
     * @param Item $child the child item that is to be added to the hierarchy
200
     * @return bool whether a loop exists
201
     */
202 44
    protected function detectLoop($parent, $child)
203
    {
204 44
        if ($child->name === $parent->name) {
205 1
            return true;
206
        }
207 44
        if (!isset($this->children[$child->name], $this->items[$parent->name])) {
208 44
            return false;
209
        }
210 43
        foreach ($this->children[$child->name] as $grandchild) {
211
            /* @var $grandchild Item */
212 43
            if ($this->detectLoop($parent, $grandchild)) {
213 43
                return true;
214
            }
215
        }
216
217 43
        return false;
218
    }
219
220
    /**
221
     * @inheritdoc
222
     */
223
    public function removeChild($parent, $child)
224
    {
225
        if (isset($this->children[$parent->name][$child->name])) {
226
            unset($this->children[$parent->name][$child->name]);
227
            $this->saveItems();
228
            return true;
229
        }
230
231
        return false;
232
    }
233
234
    /**
235
     * @inheritdoc
236
     */
237
    public function removeChildren($parent)
238
    {
239
        if (isset($this->children[$parent->name])) {
240
            unset($this->children[$parent->name]);
241
            $this->saveItems();
242
            return true;
243
        }
244
245
        return false;
246
    }
247
248
    /**
249
     * @inheritdoc
250
     */
251
    public function hasChild($parent, $child)
252
    {
253
        return isset($this->children[$parent->name][$child->name]);
254
    }
255
256
    /**
257
     * @inheritdoc
258
     */
259 45
    public function assign($role, $userId)
260
    {
261 45
        if (!isset($this->items[$role->name])) {
262
            throw new InvalidArgumentException("Unknown role '{$role->name}'.");
263 45
        } elseif (isset($this->assignments[$userId][$role->name])) {
264
            throw new InvalidArgumentException("Authorization item '{$role->name}' has already been assigned to user '$userId'.");
265
        }
266
267 45
        $this->assignments[$userId][$role->name] = new Assignment([
268 45
            'userId' => $userId,
269 45
            'roleName' => $role->name,
270 45
            'createdAt' => time(),
271
        ]);
272 45
        $this->saveAssignments();
273
274 45
        return $this->assignments[$userId][$role->name];
275
    }
276
277
    /**
278
     * @inheritdoc
279
     */
280
    public function revoke($role, $userId)
281
    {
282
        if (isset($this->assignments[$userId][$role->name])) {
283
            unset($this->assignments[$userId][$role->name]);
284
            $this->saveAssignments();
285
            return true;
286
        }
287
288
        return false;
289
    }
290
291
    /**
292
     * @inheritdoc
293
     */
294
    public function revokeAll($userId)
295
    {
296
        if (isset($this->assignments[$userId]) && is_array($this->assignments[$userId])) {
297
            foreach ($this->assignments[$userId] as $itemName => $value) {
298
                unset($this->assignments[$userId][$itemName]);
299
            }
300
            $this->saveAssignments();
301
            return true;
302
        }
303
304
        return false;
305
    }
306
307
    /**
308
     * @inheritdoc
309
     */
310
    public function getAssignment($roleName, $userId)
311
    {
312
        return isset($this->assignments[$userId][$roleName]) ? $this->assignments[$userId][$roleName] : null;
313
    }
314
315
    /**
316
     * @inheritdoc
317
     */
318 4
    public function getItems($type)
319
    {
320 4
        $items = [];
321
322 4
        foreach ($this->items as $name => $item) {
323
            /* @var $item Item */
324 4
            if ($item->type == $type) {
325 4
                $items[$name] = $item;
326
            }
327
        }
328
329 4
        return $items;
330
    }
331
332
333
    /**
334
     * @inheritdoc
335
     */
336 2
    public function removeItem($item)
337
    {
338 2
        if (isset($this->items[$item->name])) {
339 2
            foreach ($this->children as &$children) {
340 1
                unset($children[$item->name]);
341
            }
342 2
            foreach ($this->assignments as &$assignments) {
343 2
                unset($assignments[$item->name]);
344
            }
345 2
            unset($this->items[$item->name]);
346 2
            $this->saveItems();
347 2
            $this->saveAssignments();
348 2
            return true;
349
        }
350
351
        return false;
352
    }
353
354
    /**
355
     * @inheritdoc
356
     */
357 13
    public function getItem($name)
358
    {
359 13
        return isset($this->items[$name]) ? $this->items[$name] : null;
360
    }
361
362
    /**
363
     * @inheritdoc
364
     */
365 1
    public function updateRule($name, $rule)
366
    {
367 1
        if ($rule->name !== $name) {
368 1
            unset($this->rules[$name]);
369
        }
370 1
        $this->rules[$rule->name] = $rule;
371 1
        $this->saveRules();
372 1
        return true;
373
    }
374
375
    /**
376
     * @inheritdoc
377
     */
378 45
    public function getRule($name)
379
    {
380 45
        return isset($this->rules[$name]) ? $this->rules[$name] : null;
381
    }
382
383
    /**
384
     * @inheritdoc
385
     */
386 5
    public function getRules()
387
    {
388 5
        return $this->rules;
389
    }
390
391
    /**
392
     * @inheritdoc
393
     * The roles returned by this method include the roles assigned via [[$defaultRoles]].
394
     */
395 2
    public function getRolesByUser($userId)
396
    {
397 2
        $roles = $this->getDefaultRoleInstances();
398 2
        foreach ($this->getAssignments($userId) as $name => $assignment) {
399 2
            $role = $this->items[$assignment->roleName];
400 2
            if ($role->type === Item::TYPE_ROLE) {
401 2
                $roles[$name] = $role;
402
            }
403
        }
404
405 2
        return $roles;
406
    }
407
408
    /**
409
     * @inheritdoc
410
     */
411 1
    public function getChildRoles($roleName)
412
    {
413 1
        $role = $this->getRole($roleName);
414
415 1
        if ($role === null) {
416
            throw new InvalidArgumentException("Role \"$roleName\" not found.");
417
        }
418
419 1
        $result = [];
420 1
        $this->getChildrenRecursive($roleName, $result);
421
422 1
        $roles = [$roleName => $role];
423
424 1
        $roles += array_filter($this->getRoles(), function (Role $roleItem) use ($result) {
425 1
            return array_key_exists($roleItem->name, $result);
426 1
        });
427
428 1
        return $roles;
429
    }
430
431
    /**
432
     * @inheritdoc
433
     */
434 1
    public function getPermissionsByRole($roleName)
435
    {
436 1
        $result = [];
437 1
        $this->getChildrenRecursive($roleName, $result);
438 1
        if (empty($result)) {
439
            return [];
440
        }
441 1
        $permissions = [];
442 1
        foreach (array_keys($result) as $itemName) {
443 1
            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
444 1
                $permissions[$itemName] = $this->items[$itemName];
445
            }
446
        }
447 1
        return $permissions;
448
    }
449
450
    /**
451
     * Recursively finds all children and grand children of the specified item.
452
     *
453
     * @param string $name the name of the item whose children are to be looked for.
454
     * @param array $result the children and grand children (in array keys)
455
     */
456 3
    protected function getChildrenRecursive($name, &$result)
457
    {
458 3
        if (isset($this->children[$name])) {
459 3
            foreach ($this->children[$name] as $child) {
460 3
                $result[$child->name] = true;
461 3
                $this->getChildrenRecursive($child->name, $result);
462
            }
463
        }
464 3
    }
465
466
    /**
467
     * @inheritdoc
468
     */
469 1
    public function getPermissionsByUser($userId)
470
    {
471 1
        $directPermission = $this->getDirectPermissionsByUser($userId);
472 1
        $inheritedPermission = $this->getInheritedPermissionsByUser($userId);
473
474 1
        return array_merge($directPermission, $inheritedPermission);
475
    }
476
477
    /**
478
     * Returns all permissions that are directly assigned to user.
479
     * @param string|int $userId the user ID (see [[\yii\web\User::id]])
480
     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.
481
     * @since 2.0.7
482
     */
483 1
    protected function getDirectPermissionsByUser($userId)
484
    {
485 1
        $permissions = [];
486 1
        foreach ($this->getAssignments($userId) as $name => $assignment) {
487 1
            $permission = $this->items[$assignment->roleName];
488 1
            if ($permission->type === Item::TYPE_PERMISSION) {
489 1
                $permissions[$name] = $permission;
490
            }
491
        }
492
493 1
        return $permissions;
494
    }
495
496
    /**
497
     * Returns all permissions that the user inherits from the roles assigned to him.
498
     * @param string|int $userId the user ID (see [[\yii\web\User::id]])
499
     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.
500
     * @since 2.0.7
501
     */
502 1
    protected function getInheritedPermissionsByUser($userId)
503
    {
504 1
        $assignments = $this->getAssignments($userId);
505 1
        $result = [];
506 1
        foreach (array_keys($assignments) as $roleName) {
507 1
            $this->getChildrenRecursive($roleName, $result);
508
        }
509
510 1
        if (empty($result)) {
511
            return [];
512
        }
513
514 1
        $permissions = [];
515 1
        foreach (array_keys($result) as $itemName) {
516 1
            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
517 1
                $permissions[$itemName] = $this->items[$itemName];
518
            }
519
        }
520 1
        return $permissions;
521
    }
522
523
    /**
524
     * @inheritdoc
525
     */
526 1
    public function getChildren($name)
527
    {
528 1
        return isset($this->children[$name]) ? $this->children[$name] : [];
529
    }
530
531
    /**
532
     * @inheritdoc
533
     */
534 3
    public function removeAll()
535
    {
536 3
        $this->children = [];
537 3
        $this->items = [];
538 3
        $this->assignments = [];
539 3
        $this->rules = [];
540 3
        $this->save();
541 3
    }
542
543
    /**
544
     * @inheritdoc
545
     */
546 1
    public function removeAllPermissions()
547
    {
548 1
        $this->removeAllItems(Item::TYPE_PERMISSION);
549 1
    }
550
551
    /**
552
     * @inheritdoc
553
     */
554 1
    public function removeAllRoles()
555
    {
556 1
        $this->removeAllItems(Item::TYPE_ROLE);
557 1
    }
558
559
    /**
560
     * Removes all auth items of the specified type.
561
     * @param int $type the auth item type (either Item::TYPE_PERMISSION or Item::TYPE_ROLE)
562
     */
563 2
    protected function removeAllItems($type)
564
    {
565 2
        $names = [];
566 2
        foreach ($this->items as $name => $item) {
567 2
            if ($item->type == $type) {
568 2
                unset($this->items[$name]);
569 2
                $names[$name] = true;
570
            }
571
        }
572 2
        if (empty($names)) {
573
            return;
574
        }
575
576 2
        foreach ($this->assignments as $i => $assignments) {
577 2
            foreach ($assignments as $n => $assignment) {
578 2
                if (isset($names[$assignment->roleName])) {
579 2
                    unset($this->assignments[$i][$n]);
580
                }
581
            }
582
        }
583 2
        foreach ($this->children as $name => $children) {
584 2
            if (isset($names[$name])) {
585 1
                unset($this->children[$name]);
586
            } else {
587 1
                foreach ($children as $childName => $item) {
588 1
                    if (isset($names[$childName])) {
589 1
                        unset($children[$childName]);
590
                    }
591
                }
592 2
                $this->children[$name] = $children;
593
            }
594
        }
595
596 2
        $this->saveItems();
597 2
    }
598
599
    /**
600
     * @inheritdoc
601
     */
602 1
    public function removeAllRules()
603
    {
604 1
        foreach ($this->items as $item) {
605 1
            $item->ruleName = null;
606
        }
607 1
        $this->rules = [];
608 1
        $this->saveRules();
609 1
    }
610
611
    /**
612
     * @inheritdoc
613
     */
614
    public function removeAllAssignments()
615
    {
616
        $this->assignments = [];
617
        $this->saveAssignments();
618
    }
619
620
    /**
621
     * @inheritdoc
622
     */
623 1
    protected function removeRule($rule)
624
    {
625 1
        if (isset($this->rules[$rule->name])) {
626 1
            unset($this->rules[$rule->name]);
627 1
            foreach ($this->items as $item) {
628 1
                if ($item->ruleName === $rule->name) {
629 1
                    $item->ruleName = null;
630
                }
631
            }
632 1
            $this->saveRules();
633 1
            return true;
634
        }
635
636
        return false;
637
    }
638
639
    /**
640
     * @inheritdoc
641
     */
642 46
    protected function addRule($rule)
643
    {
644 46
        $this->rules[$rule->name] = $rule;
645 46
        $this->saveRules();
646 46
        return true;
647
    }
648
649
    /**
650
     * @inheritdoc
651
     */
652 6
    protected function updateItem($name, $item)
653
    {
654 6
        if ($name !== $item->name) {
655 5
            if (isset($this->items[$item->name])) {
656 1
                throw new InvalidArgumentException("Unable to change the item name. The name '{$item->name}' is already used by another item.");
657
            }
658
659
            // Remove old item in case of renaming
660 4
            unset($this->items[$name]);
661
662 4
            if (isset($this->children[$name])) {
663
                $this->children[$item->name] = $this->children[$name];
664
                unset($this->children[$name]);
665
            }
666 4
            foreach ($this->children as &$children) {
667 2
                if (isset($children[$name])) {
668 2
                    $children[$item->name] = $children[$name];
669 2
                    unset($children[$name]);
670
                }
671
            }
672 4
            foreach ($this->assignments as &$assignments) {
673 4
                if (isset($assignments[$name])) {
674 2
                    $assignments[$item->name] = $assignments[$name];
675 2
                    $assignments[$item->name]->roleName = $item->name;
676 4
                    unset($assignments[$name]);
677
                }
678
            }
679 4
            $this->saveAssignments();
680
        }
681
682 5
        $this->items[$item->name] = $item;
683
684 5
        $this->saveItems();
685 5
        return true;
686
    }
687
688
    /**
689
     * @inheritdoc
690
     */
691 47
    protected function addItem($item)
692
    {
693 47
        $time = time();
694 47
        if ($item->createdAt === null) {
695 47
            $item->createdAt = $time;
696
        }
697 47
        if ($item->updatedAt === null) {
698 47
            $item->updatedAt = $time;
699
        }
700
701 47
        $this->items[$item->name] = $item;
702
703 47
        $this->saveItems();
704
705 47
        return true;
706
    }
707
708
    /**
709
     * Loads authorization data from persistent storage.
710
     */
711 50
    protected function load()
712
    {
713 50
        $this->children = [];
714 50
        $this->rules = [];
715 50
        $this->assignments = [];
716 50
        $this->items = [];
717
718 50
        $items = $this->loadFromFile($this->itemFile);
719 50
        $itemsMtime = @filemtime($this->itemFile);
720 50
        $assignments = $this->loadFromFile($this->assignmentFile);
721 50
        $assignmentsMtime = @filemtime($this->assignmentFile);
722 50
        $rules = $this->loadFromFile($this->ruleFile);
723
724 50
        foreach ($items as $name => $item) {
725 4
            $class = $item['type'] == Item::TYPE_PERMISSION ? Permission::class : Role::class;
726
727 4
            $this->items[$name] = new $class([
728 4
                'name' => $name,
729 4
                'description' => isset($item['description']) ? $item['description'] : null,
730 4
                'ruleName' => isset($item['ruleName']) ? $item['ruleName'] : null,
731 4
                'data' => isset($item['data']) ? $item['data'] : null,
732 4
                'createdAt' => $itemsMtime,
733 4
                'updatedAt' => $itemsMtime,
734
            ]);
735
        }
736
737 50
        foreach ($items as $name => $item) {
738 4
            if (isset($item['children'])) {
739 4
                foreach ($item['children'] as $childName) {
740 4
                    if (isset($this->items[$childName])) {
741 4
                        $this->children[$name][$childName] = $this->items[$childName];
742
                    }
743
                }
744
            }
745
        }
746
747 50
        foreach ($assignments as $userId => $roles) {
748 4
            foreach ($roles as $role) {
749 4
                $this->assignments[$userId][$role] = new Assignment([
750 4
                    'userId' => $userId,
751 4
                    'roleName' => $role,
752 4
                    'createdAt' => $assignmentsMtime,
753
                ]);
754
            }
755
        }
756
757 50
        foreach ($rules as $name => $ruleData) {
758 4
            $this->rules[$name] = unserialize($ruleData);
759
        }
760 50
    }
761
762
    /**
763
     * Saves authorization data into persistent storage.
764
     */
765 4
    protected function save()
766
    {
767 4
        $this->saveItems();
768 4
        $this->saveAssignments();
769 4
        $this->saveRules();
770 4
    }
771
772
    /**
773
     * Loads the authorization data from a PHP script file.
774
     *
775
     * @param string $file the file path.
776
     * @return array the authorization data
777
     * @see saveToFile()
778
     */
779 50
    protected function loadFromFile($file)
780
    {
781 50
        if (is_file($file)) {
782 4
            return require $file;
783
        }
784
785 50
        return [];
786
    }
787
788
    /**
789
     * Saves the authorization data to a PHP script file.
790
     *
791
     * @param array $data the authorization data
792
     * @param string $file the file path.
793
     * @see loadFromFile()
794
     */
795 28
    protected function saveToFile($data, $file)
796
    {
797 28
        file_put_contents($file, "<?php\nreturn " . VarDumper::export($data) . ";\n", LOCK_EX);
798 28
        $this->invalidateScriptCache($file);
799 28
    }
800
801
    /**
802
     * Invalidates precompiled script cache (such as OPCache or APC) for the given file.
803
     * @param string $file the file path.
804
     * @since 2.0.9
805
     */
806 28
    protected function invalidateScriptCache($file)
807
    {
808 28
        if (function_exists('opcache_invalidate')) {
809 28
            opcache_invalidate($file, true);
810
        }
811 28
        if (function_exists('apc_delete_file')) {
812
            @apc_delete_file($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
813
        }
814 28
    }
815
816
    /**
817
     * Saves items data into persistent storage.
818
     */
819 48
    protected function saveItems()
820
    {
821 48
        $items = [];
822 48
        foreach ($this->items as $name => $item) {
823
            /* @var $item Item */
824 47
            $items[$name] = array_filter(
825
                [
826 47
                    'type' => $item->type,
827 47
                    'description' => $item->description,
828 47
                    'ruleName' => $item->ruleName,
829 47
                    'data' => $item->data,
830
                ]
831
            );
832 47
            if (isset($this->children[$name])) {
833 44
                foreach ($this->children[$name] as $child) {
834
                    /* @var $child Item */
835 47
                    $items[$name]['children'][] = $child->name;
836
                }
837
            }
838
        }
839 48
        $this->saveToFile($items, $this->itemFile);
840 48
    }
841
842
    /**
843
     * Saves assignments data into persistent storage.
844
     */
845 46
    protected function saveAssignments()
846
    {
847 46
        $assignmentData = [];
848 46
        foreach ($this->assignments as $userId => $assignments) {
849 45
            foreach ($assignments as $name => $assignment) {
850
                /* @var $assignment Assignment */
851 45
                $assignmentData[$userId][] = $assignment->roleName;
852
            }
853
        }
854 46
        $this->saveToFile($assignmentData, $this->assignmentFile);
855 46
    }
856
857
    /**
858
     * Saves rules data into persistent storage.
859
     */
860 47
    protected function saveRules()
861
    {
862 47
        $rules = [];
863 47
        foreach ($this->rules as $name => $rule) {
864 46
            $rules[$name] = serialize($rule);
865
        }
866 47
        $this->saveToFile($rules, $this->ruleFile);
867 47
    }
868
869
    /**
870
     * @inheritdoc
871
     * @since 2.0.7
872
     */
873 1
    public function getUserIdsByRole($roleName)
874
    {
875 1
        $result = [];
876 1
        foreach ($this->assignments as $userID => $assignments) {
877 1
            foreach ($assignments as $userAssignment) {
878 1
                if ($userAssignment->roleName === $roleName && $userAssignment->userId == $userID) {
879 1
                    $result[] = (string) $userID;
880
                }
881
            }
882
        }
883 1
        return $result;
884
    }
885
}
886