GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 66e815...df733d )
by Robert
16:26
created

PhpManager   D

Complexity

Total Complexity 149

Size/Duplication

Total Lines 848
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 87.44%

Importance

Changes 0
Metric Value
wmc 149
lcom 1
cbo 10
dl 0
loc 848
ccs 362
cts 414
cp 0.8744
rs 4.4444
c 0
b 0
f 0

47 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 8 1
A checkAccess() 0 10 2
A getAssignments() 0 4 2
D checkAccessRecursive() 0 26 9
A canAddChild() 0 4 1
C addChild() 0 24 7
B detectLoop() 0 17 5
A removeChild() 0 10 2
A removeChildren() 0 10 2
A hasChild() 0 4 1
A assign() 0 16 3
A revoke() 0 10 2
A revokeAll() 0 12 4
A getAssignment() 0 4 2
A getItems() 0 13 3
A removeItem() 0 17 4
A getItem() 0 4 2
A updateRule() 0 9 2
A getRule() 0 4 2
A getRules() 0 4 1
A getRolesByUser() 0 12 3
A getChildRoles() 0 19 2
B getPermissionsByRole() 0 15 5
A getChildrenRecursive() 0 9 3
A getPermissionsByUser() 0 7 1
A getDirectPermissionsByUser() 0 12 3
B getInheritedPermissionsByUser() 0 20 6
A getChildren() 0 4 2
A removeAll() 0 8 1
A removeAllPermissions() 0 4 1
A removeAllRoles() 0 4 1
C removeAllItems() 0 35 11
A removeAllRules() 0 8 2
A removeAllAssignments() 0 5 1
A removeRule() 0 15 4
A addRule() 0 6 1
C updateItem() 0 35 8
A addItem() 0 17 3
C load() 0 50 13
A save() 0 6 1
A loadFromFile() 0 8 2
A saveToFile() 0 5 1
A invalidateScriptCache() 0 9 3
B saveItems() 0 22 4
A saveAssignments() 0 11 3
A saveRules() 0 8 2
B getUserIdsByRole() 0 12 5

How to fix   Complexity   

Complex Class

Complex classes like PhpManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PhpManager, and based on these observations, apply Extract Interface, too.

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\base\InvalidCallException;
11
use yii\base\InvalidParamException;
12
use Yii;
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 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 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 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 28
    public function init()
88
    {
89 28
        parent::init();
90 28
        $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 28
        $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 28
        $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 28
        $this->load();
94 28
    }
95
96
    /**
97
     * @inheritdoc
98
     */
99 2
    public function checkAccess($userId, $permissionName, $params = [])
100
    {
101 2
        $assignments = $this->getAssignments($userId);
102
103 2
        if ($this->hasNoAssignments($assignments)) {
104 1
            return false;
105
        }
106
107 2
        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);
108
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113 6
    public function getAssignments($userId)
114
    {
115 6
        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 2
    protected function checkAccessRecursive($user, $itemName, $params, $assignments)
132
    {
133 2
        if (!isset($this->items[$itemName])) {
134 1
            return false;
135
        }
136
137
        /* @var $item Item */
138 2
        $item = $this->items[$itemName];
139 2
        Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);
140
141 2
        if (!$this->executeRule($user, $item, $params)) {
142 2
            return false;
143
        }
144
145 2
        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {
146 2
            return true;
147
        }
148
149 1
        foreach ($this->children as $parentName => $children) {
150 1
            if (isset($children[$itemName]) && $this->checkAccessRecursive($user, $parentName, $params, $assignments)) {
151 1
                return true;
152
            }
153 1
        }
154
155 1
        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 22
    public function addChild($parent, $child)
171
    {
172 22
        if (!isset($this->items[$parent->name], $this->items[$child->name])) {
173
            throw new InvalidParamException("Either '{$parent->name}' or '{$child->name}' does not exist.");
174
        }
175
176 22
        if ($parent->name === $child->name) {
177
            throw new InvalidParamException("Cannot add '{$parent->name} ' as a child of itself.");
178
        }
179 22
        if ($parent instanceof Permission && $child instanceof Role) {
180
            throw new InvalidParamException('Cannot add a role as a child of a permission.');
181
        }
182
183 22
        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 22
        if (isset($this->children[$parent->name][$child->name])) {
187
            throw new InvalidCallException("The item '{$parent->name}' already has a child '{$child->name}'.");
188
        }
189 22
        $this->children[$parent->name][$child->name] = $this->items[$child->name];
190 22
        $this->saveItems();
191
192 22
        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 22
    protected function detectLoop($parent, $child)
203
    {
204 22
        if ($child->name === $parent->name) {
205 1
            return true;
206
        }
207 22
        if (!isset($this->children[$child->name], $this->items[$parent->name])) {
208 22
            return false;
209
        }
210 21
        foreach ($this->children[$child->name] as $grandchild) {
211
            /* @var $grandchild Item */
212 21
            if ($this->detectLoop($parent, $grandchild)) {
213 1
                return true;
214
            }
215 21
        }
216
217 21
        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
        } else {
230
            return false;
231
        }
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
        } else {
244
            return false;
245
        }
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 23
    public function assign($role, $userId)
260
    {
261 23
        if (!isset($this->items[$role->name])) {
262
            throw new InvalidParamException("Unknown role '{$role->name}'.");
263 23
        } elseif (isset($this->assignments[$userId][$role->name])) {
264
            throw new InvalidParamException("Authorization item '{$role->name}' has already been assigned to user '$userId'.");
265
        } else {
266 23
            $this->assignments[$userId][$role->name] = new Assignment([
267 23
                'userId' => $userId,
268 23
                'roleName' => $role->name,
269 23
                'createdAt' => time(),
270 23
            ]);
271 23
            $this->saveAssignments();
272 23
            return $this->assignments[$userId][$role->name];
273
        }
274
    }
275
276
    /**
277
     * @inheritdoc
278
     */
279
    public function revoke($role, $userId)
280
    {
281
        if (isset($this->assignments[$userId][$role->name])) {
282
            unset($this->assignments[$userId][$role->name]);
283
            $this->saveAssignments();
284
            return true;
285
        } else {
286
            return false;
287
        }
288
    }
289
290
    /**
291
     * @inheritdoc
292
     */
293
    public function revokeAll($userId)
294
    {
295
        if (isset($this->assignments[$userId]) && is_array($this->assignments[$userId])) {
296
            foreach ($this->assignments[$userId] as $itemName => $value) {
297
                unset($this->assignments[$userId][$itemName]);
298
            }
299
            $this->saveAssignments();
300
            return true;
301
        } else {
302
            return false;
303
        }
304
    }
305
306
    /**
307
     * @inheritdoc
308
     */
309
    public function getAssignment($roleName, $userId)
310
    {
311
        return isset($this->assignments[$userId][$roleName]) ? $this->assignments[$userId][$roleName] : null;
312
    }
313
314
    /**
315
     * @inheritdoc
316
     */
317 4
    public function getItems($type)
318
    {
319 4
        $items = [];
320
321 4
        foreach ($this->items as $name => $item) {
322
            /* @var $item Item */
323 4
            if ($item->type == $type) {
324 4
                $items[$name] = $item;
325 4
            }
326 4
        }
327
328 4
        return $items;
329
    }
330
331
332
    /**
333
     * @inheritdoc
334
     */
335 2
    public function removeItem($item)
336
    {
337 2
        if (isset($this->items[$item->name])) {
338 2
            foreach ($this->children as &$children) {
339 1
                unset($children[$item->name]);
340 2
            }
341 2
            foreach ($this->assignments as &$assignments) {
342 2
                unset($assignments[$item->name]);
343 2
            }
344 2
            unset($this->items[$item->name]);
345 2
            $this->saveItems();
346 2
            $this->saveAssignments();
347 2
            return true;
348
        } else {
349
            return false;
350
        }
351
    }
352
353
    /**
354
     * @inheritdoc
355
     */
356 11
    public function getItem($name)
357
    {
358 11
        return isset($this->items[$name]) ? $this->items[$name] : null;
359
    }
360
361
    /**
362
     * @inheritdoc
363
     */
364 1
    public function updateRule($name, $rule)
365
    {
366 1
        if ($rule->name !== $name) {
367 1
            unset($this->rules[$name]);
368 1
        }
369 1
        $this->rules[$rule->name] = $rule;
370 1
        $this->saveRules();
371 1
        return true;
372
    }
373
374
    /**
375
     * @inheritdoc
376
     */
377 23
    public function getRule($name)
378
    {
379 23
        return isset($this->rules[$name]) ? $this->rules[$name] : null;
380
    }
381
382
    /**
383
     * @inheritdoc
384
     */
385 5
    public function getRules()
386
    {
387 5
        return $this->rules;
388
    }
389
390
    /**
391
     * @inheritdoc
392
     */
393 2
    public function getRolesByUser($userId)
394
    {
395 2
        $roles = [];
396 2
        foreach ($this->getAssignments($userId) as $name => $assignment) {
397 2
            $role = $this->items[$assignment->roleName];
398 2
            if ($role->type === Item::TYPE_ROLE) {
399 2
                $roles[$name] = $role;
400 2
            }
401 2
        }
402
403 2
        return $roles;
404
    }
405
406
    /**
407
     * @inheritdoc
408
     */
409 1
    public function getChildRoles($roleName)
410
    {
411 1
        $role = $this->getRole($roleName);
412
413 1
        if (is_null($role)) {
414
            throw new InvalidParamException("Role \"$roleName\" not found.");
415
        }
416
417 1
        $result = [];
418 1
        $this->getChildrenRecursive($roleName, $result);
419
420 1
        $roles = [$roleName => $role];
421
422 1
        $roles += array_filter($this->getRoles(), function (Role $roleItem) use ($result) {
423 1
            return array_key_exists($roleItem->name, $result);
424 1
        });
425
426 1
        return $roles;
427
    }
428
429
    /**
430
     * @inheritdoc
431
     */
432 1
    public function getPermissionsByRole($roleName)
433
    {
434 1
        $result = [];
435 1
        $this->getChildrenRecursive($roleName, $result);
436 1
        if (empty($result)) {
437
            return [];
438
        }
439 1
        $permissions = [];
440 1
        foreach (array_keys($result) as $itemName) {
441 1
            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
442 1
                $permissions[$itemName] = $this->items[$itemName];
443 1
            }
444 1
        }
445 1
        return $permissions;
446
    }
447
448
    /**
449
     * Recursively finds all children and grand children of the specified item.
450
     *
451
     * @param string $name the name of the item whose children are to be looked for.
452
     * @param array $result the children and grand children (in array keys)
453
     */
454 3
    protected function getChildrenRecursive($name, &$result)
455
    {
456 3
        if (isset($this->children[$name])) {
457 3
            foreach ($this->children[$name] as $child) {
458 3
                $result[$child->name] = true;
459 3
                $this->getChildrenRecursive($child->name, $result);
460 3
            }
461 3
        }
462 3
    }
463
464
    /**
465
     * @inheritdoc
466
     */
467 1
    public function getPermissionsByUser($userId)
468
    {
469 1
        $directPermission = $this->getDirectPermissionsByUser($userId);
470 1
        $inheritedPermission = $this->getInheritedPermissionsByUser($userId);
471
472 1
        return array_merge($directPermission, $inheritedPermission);
473
    }
474
475
    /**
476
     * Returns all permissions that are directly assigned to user.
477
     * @param string|int $userId the user ID (see [[\yii\web\User::id]])
478
     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.
479
     * @since 2.0.7
480
     */
481 1
    protected function getDirectPermissionsByUser($userId)
482
    {
483 1
        $permissions = [];
484 1
        foreach ($this->getAssignments($userId) as $name => $assignment) {
485 1
            $permission = $this->items[$assignment->roleName];
486 1
            if ($permission->type === Item::TYPE_PERMISSION) {
487 1
                $permissions[$name] = $permission;
488 1
            }
489 1
        }
490
491 1
        return $permissions;
492
    }
493
494
    /**
495
     * Returns all permissions that the user inherits from the roles assigned to him.
496
     * @param string|int $userId the user ID (see [[\yii\web\User::id]])
497
     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.
498
     * @since 2.0.7
499
     */
500 1
    protected function getInheritedPermissionsByUser($userId)
501
    {
502 1
        $assignments = $this->getAssignments($userId);
503 1
        $result = [];
504 1
        foreach (array_keys($assignments) as $roleName) {
505 1
            $this->getChildrenRecursive($roleName, $result);
506 1
        }
507
508 1
        if (empty($result)) {
509
            return [];
510
        }
511
512 1
        $permissions = [];
513 1
        foreach (array_keys($result) as $itemName) {
514 1
            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
515 1
                $permissions[$itemName] = $this->items[$itemName];
516 1
            }
517 1
        }
518 1
        return $permissions;
519
    }
520
521
    /**
522
     * @inheritdoc
523
     */
524 1
    public function getChildren($name)
525
    {
526 1
        return isset($this->children[$name]) ? $this->children[$name] : [];
527
    }
528
529
    /**
530
     * @inheritdoc
531
     */
532 3
    public function removeAll()
533
    {
534 3
        $this->children = [];
535 3
        $this->items = [];
536 3
        $this->assignments = [];
537 3
        $this->rules = [];
538 3
        $this->save();
539 3
    }
540
541
    /**
542
     * @inheritdoc
543
     */
544 1
    public function removeAllPermissions()
545
    {
546 1
        $this->removeAllItems(Item::TYPE_PERMISSION);
547 1
    }
548
549
    /**
550
     * @inheritdoc
551
     */
552 1
    public function removeAllRoles()
553
    {
554 1
        $this->removeAllItems(Item::TYPE_ROLE);
555 1
    }
556
557
    /**
558
     * Removes all auth items of the specified type.
559
     * @param int $type the auth item type (either Item::TYPE_PERMISSION or Item::TYPE_ROLE)
560
     */
561 2
    protected function removeAllItems($type)
562
    {
563 2
        $names = [];
564 2
        foreach ($this->items as $name => $item) {
565 2
            if ($item->type == $type) {
566 2
                unset($this->items[$name]);
567 2
                $names[$name] = true;
568 2
            }
569 2
        }
570 2
        if (empty($names)) {
571
            return;
572
        }
573
574 2
        foreach ($this->assignments as $i => $assignments) {
575 2
            foreach ($assignments as $n => $assignment) {
576 2
                if (isset($names[$assignment->roleName])) {
577 2
                    unset($this->assignments[$i][$n]);
578 2
                }
579 2
            }
580 2
        }
581 2
        foreach ($this->children as $name => $children) {
582 2
            if (isset($names[$name])) {
583 1
                unset($this->children[$name]);
584 1
            } else {
585 1
                foreach ($children as $childName => $item) {
586 1
                    if (isset($names[$childName])) {
587 1
                        unset($children[$childName]);
588 1
                    }
589 1
                }
590 1
                $this->children[$name] = $children;
591
            }
592 2
        }
593
594 2
        $this->saveItems();
595 2
    }
596
597
    /**
598
     * @inheritdoc
599
     */
600 1
    public function removeAllRules()
601
    {
602 1
        foreach ($this->items as $item) {
603 1
            $item->ruleName = null;
604 1
        }
605 1
        $this->rules = [];
606 1
        $this->saveRules();
607 1
    }
608
609
    /**
610
     * @inheritdoc
611
     */
612
    public function removeAllAssignments()
613
    {
614
        $this->assignments = [];
615
        $this->saveAssignments();
616
    }
617
618
    /**
619
     * @inheritdoc
620
     */
621 1
    protected function removeRule($rule)
622
    {
623 1
        if (isset($this->rules[$rule->name])) {
624 1
            unset($this->rules[$rule->name]);
625 1
            foreach ($this->items as $item) {
626 1
                if ($item->ruleName === $rule->name) {
627 1
                    $item->ruleName = null;
628 1
                }
629 1
            }
630 1
            $this->saveRules();
631 1
            return true;
632
        } else {
633
            return false;
634
        }
635
    }
636
637
    /**
638
     * @inheritdoc
639
     */
640 24
    protected function addRule($rule)
641
    {
642 24
        $this->rules[$rule->name] = $rule;
643 24
        $this->saveRules();
644 24
        return true;
645
    }
646
647
    /**
648
     * @inheritdoc
649
     */
650 6
    protected function updateItem($name, $item)
651
    {
652 6
        if ($name !== $item->name) {
653 5
            if (isset($this->items[$item->name])) {
654 1
                throw new InvalidParamException("Unable to change the item name. The name '{$item->name}' is already used by another item.");
655
            } else {
656
                // Remove old item in case of renaming
657 4
                unset($this->items[$name]);
658
659 4
                if (isset($this->children[$name])) {
660
                    $this->children[$item->name] = $this->children[$name];
661
                    unset($this->children[$name]);
662
                }
663 4
                foreach ($this->children as &$children) {
664 2
                    if (isset($children[$name])) {
665 2
                        $children[$item->name] = $children[$name];
666 2
                        unset($children[$name]);
667 2
                    }
668 4
                }
669 4
                foreach ($this->assignments as &$assignments) {
670 4
                    if (isset($assignments[$name])) {
671 2
                        $assignments[$item->name] = $assignments[$name];
672 2
                        $assignments[$item->name]->roleName = $item->name;
673 2
                        unset($assignments[$name]);
674 2
                    }
675 4
                }
676 4
                $this->saveAssignments();
677
            }
678 4
        }
679
680 5
        $this->items[$item->name] = $item;
681
682 5
        $this->saveItems();
683 5
        return true;
684
    }
685
686
    /**
687
     * @inheritdoc
688
     */
689 25
    protected function addItem($item)
690
    {
691 25
        $time = time();
692 25
        if ($item->createdAt === null) {
693 25
            $item->createdAt = $time;
694 25
        }
695 25
        if ($item->updatedAt === null) {
696 25
            $item->updatedAt = $time;
697 25
        }
698
699 25
        $this->items[$item->name] = $item;
700
701 25
        $this->saveItems();
702
703 25
        return true;
704
705
    }
706
707
    /**
708
     * Loads authorization data from persistent storage.
709
     */
710 28
    protected function load()
711
    {
712 28
        $this->children = [];
713 28
        $this->rules = [];
714 28
        $this->assignments = [];
715 28
        $this->items = [];
716
717 28
        $items = $this->loadFromFile($this->itemFile);
718 28
        $itemsMtime = @filemtime($this->itemFile);
719 28
        $assignments = $this->loadFromFile($this->assignmentFile);
720 28
        $assignmentsMtime = @filemtime($this->assignmentFile);
721 28
        $rules = $this->loadFromFile($this->ruleFile);
722
723 28
        foreach ($items as $name => $item) {
724 4
            $class = $item['type'] == Item::TYPE_PERMISSION ? Permission::className() : Role::className();
725
726 4
            $this->items[$name] = new $class([
727 4
                'name' => $name,
728 4
                'description' => isset($item['description']) ? $item['description'] : null,
729 4
                'ruleName' => isset($item['ruleName']) ? $item['ruleName'] : null,
730 4
                'data' => isset($item['data']) ? $item['data'] : null,
731 4
                'createdAt' => $itemsMtime,
732 4
                'updatedAt' => $itemsMtime,
733 4
            ]);
734 28
        }
735
736 28
        foreach ($items as $name => $item) {
737 4
            if (isset($item['children'])) {
738 4
                foreach ($item['children'] as $childName) {
739 4
                    if (isset($this->items[$childName])) {
740 4
                        $this->children[$name][$childName] = $this->items[$childName];
741 4
                    }
742 4
                }
743 4
            }
744 28
        }
745
746 28
        foreach ($assignments as $userId => $roles) {
747 4
            foreach ($roles as $role) {
748 4
                $this->assignments[$userId][$role] = new Assignment([
749 4
                    'userId' => $userId,
750 4
                    'roleName' => $role,
751 4
                    'createdAt' => $assignmentsMtime,
752 4
                ]);
753 4
            }
754 28
        }
755
756 28
        foreach ($rules as $name => $ruleData) {
757 4
            $this->rules[$name] = unserialize($ruleData);
758 28
        }
759 28
    }
760
761
    /**
762
     * Saves authorization data into persistent storage.
763
     */
764 4
    protected function save()
765
    {
766 4
        $this->saveItems();
767 4
        $this->saveAssignments();
768 4
        $this->saveRules();
769 4
    }
770
771
    /**
772
     * Loads the authorization data from a PHP script file.
773
     *
774
     * @param string $file the file path.
775
     * @return array the authorization data
776
     * @see saveToFile()
777
     */
778 28
    protected function loadFromFile($file)
779
    {
780 28
        if (is_file($file)) {
781 4
            return require($file);
782
        } else {
783 28
            return [];
784
        }
785
    }
786
787
    /**
788
     * Saves the authorization data to a PHP script file.
789
     *
790
     * @param array $data the authorization data
791
     * @param string $file the file path.
792
     * @see loadFromFile()
793
     */
794 26
    protected function saveToFile($data, $file)
795
    {
796 26
        file_put_contents($file, "<?php\nreturn " . VarDumper::export($data) . ";\n", LOCK_EX);
797 26
        $this->invalidateScriptCache($file);
798 26
    }
799
800
    /**
801
     * Invalidates precompiled script cache (such as OPCache or APC) for the given file.
802
     * @param string $file the file path.
803
     * @since 2.0.9
804
     */
805 26
    protected function invalidateScriptCache($file)
806
    {
807 26
        if (function_exists('opcache_invalidate')) {
808 26
            opcache_invalidate($file, true);
809 26
        }
810 26
        if (function_exists('apc_delete_file')) {
811
            @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...
812
        }
813 26
    }
814
815
    /**
816
     * Saves items data into persistent storage.
817
     */
818 26
    protected function saveItems()
819
    {
820 26
        $items = [];
821 26
        foreach ($this->items as $name => $item) {
822
            /* @var $item Item */
823 25
            $items[$name] = array_filter(
824
                [
825 25
                    'type' => $item->type,
826 25
                    'description' => $item->description,
827 25
                    'ruleName' => $item->ruleName,
828 25
                    'data' => $item->data,
829
                ]
830 25
            );
831 25
            if (isset($this->children[$name])) {
832 22
                foreach ($this->children[$name] as $child) {
833
                    /* @var $child Item */
834 22
                    $items[$name]['children'][] = $child->name;
835 22
                }
836 22
            }
837 26
        }
838 26
        $this->saveToFile($items, $this->itemFile);
839 26
    }
840
841
    /**
842
     * Saves assignments data into persistent storage.
843
     */
844 24
    protected function saveAssignments()
845
    {
846 24
        $assignmentData = [];
847 24
        foreach ($this->assignments as $userId => $assignments) {
848 23
            foreach ($assignments as $name => $assignment) {
849
                /* @var $assignment Assignment */
850 23
                $assignmentData[$userId][] = $assignment->roleName;
851 23
            }
852 24
        }
853 24
        $this->saveToFile($assignmentData, $this->assignmentFile);
854 24
    }
855
856
    /**
857
     * Saves rules data into persistent storage.
858
     */
859 25
    protected function saveRules()
860
    {
861 25
        $rules = [];
862 25
        foreach ($this->rules as $name => $rule) {
863 24
            $rules[$name] = serialize($rule);
864 25
        }
865 25
        $this->saveToFile($rules, $this->ruleFile);
866 25
    }
867
868
    /**
869
     * @inheritdoc
870
     * @since 2.0.7
871
     */
872 1
    public function getUserIdsByRole($roleName)
873
    {
874 1
        $result = [];
875 1
        foreach ($this->assignments as $userID => $assignments) {
876 1
            foreach ($assignments as $userAssignment) {
877 1
                if ($userAssignment->roleName === $roleName && $userAssignment->userId == $userID) {
878 1
                    $result[] = (string)$userID;
879 1
                }
880 1
            }
881 1
        }
882 1
        return $result;
883
    }
884
}
885