Completed
Push — master ( 5b1557...9caaaf )
by Stephen
02:27
created

UserAndLevel::getPermissions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 10
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
crap 1
1
<?php
2
3
namespace z1haze\Acl\Traits;
4
5
use Illuminate\Support\Collection;
6
use ReflectionClass;
7
use z1haze\Acl\Exceptions\PermissionNotFoundException;
8
use Illuminate\Support\Facades\Cache;
9
use z1haze\Acl\Models\Level;
10
use z1haze\Acl\Models\Permission;
11
12
trait UserAndLevel
13
{
14
    /**
15
     * USER & LEVEL
16
     * Check if a model has a given level
17
     *
18
     * @param mixed level
19
     * @return bool
20
     */
21 1
    public function isLevel($level)
22
    {
23 1
        $level = $this->getALevel($level);
0 ignored issues
show
Bug introduced by
It seems like getALevel() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
24
25 1
        return is_a($this, config('laravel-acl.level'), true) ? $this->name == $level->name : $this->level->name == $level->name;
0 ignored issues
show
Bug introduced by
The property name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property level does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
26
    }
27
28
    /**
29
     * USER & LEVEL
30
     * Check if a user has or has inherited
31
     * a given level
32
     *
33
     * @param $level
34
     * @return bool
35
     */
36 1
    public function hasLevel($level)
37
    {
38 1
        $level = $this->getLevel($level);
0 ignored issues
show
Bug introduced by
It seems like getLevel() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
39
40 1
        return is_a($this, config('laravel-acl.level'), true) ? $this->rank <= $level->rank : $this->level->rank <= $level->rank;
0 ignored issues
show
Bug introduced by
The property rank does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
41
    }
42
43
    /**
44
     * USER & LEVEL
45
     * Add a single permission to a model
46
     *
47
     * @param $permission
48
     */
49 16
    public function addPermission($permission)
50
    {
51 16
        $this->modifyPermissions([$permission], 'add');
52 15
    }
53
54
    /**
55
     * USER & LEVEL
56
     * Add an array of permissions to a model
57
     *
58
     * @param $permissions
59
     */
60 9
    public function addPermissions(array $permissions)
61
    {
62 9
        $this->modifyPermissions($permissions, 'add');
63 9
    }
64
65
    /**
66
     * USER & LEVEL
67
     * Remove a permission from a model
68
     *
69
     * @param $permission
70
     */
71 2
    public function removePermission($permission)
72
    {
73 2
        $this->modifyPermissions([$permission], 'remove');
74 2
    }
75
76
    /**
77
     * USER & LEVEL
78
     * Remove an array of permissions objects or permission names (can be mixed)
79
     *
80
     * @param $permissions
81
     */
82 1
    public function removePermissions(array $permissions)
83
    {
84 1
        $this->modifyPermissions($permissions, 'remove');
85 1
    }
86
87
    /**
88
     * USER & LEVEL
89
     * Sync permissions on a model
90
     *
91
     * @param $permissions
92
     */
93 2
    public function syncPermissions(array $permissions)
94
    {
95 2
        $this->modifyPermissions($permissions, 'sync');
96 2
    }
97
98
    /**
99
     * USER & LEVEL
100
     * Clear permissions from a model
101
     */
102 3
    public function clearPermissions()
103
    {
104 3
        if (is_a($this, config('laravel-acl.user'), true)) {
105 1
            $this->permissions()->detach();
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
106
        }
107
108 3
        if (is_a($this, config('laravel-acl.level'), true)) {
109 2
            config('laravel-acl.permission', Permission::class)::whereLevelId($this->id)->update(['level_id' => null]);
0 ignored issues
show
Bug introduced by
The property id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
110
        }
111 3
    }
112
113
    /**
114
     * USER & LEVEL
115
     * First try the cache to return the collection,
116
     * then fetch it from the database.
117
     *
118
     * See @cacheGetPermissions()
119
     *
120
     * @return \Illuminate\Support\Collection
121
     */
122 2 View Code Duplication
    public function getPermissions()
123
    {
124 2
        return Cache::remember(
125 2
            'laravel-acl.getPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
126 2
            config('laravel-acl.cacheMinutes'),
127
            function() {
128 2
                return $this->cachegetPermissions();
129 2
            }
130
        );
131
    }
132
133
    /**
134
     * USER & LEVEL
135
     * First try the cache to return the collection,
136
     * then fetch it from the database.
137
     *
138
     * See @cacheGetAllPermissions()
139
     *
140
     * @return \Illuminate\Support\Collection
141
     */
142 7 View Code Duplication
    public function getAllPermissions()
143
    {
144 7
        return Cache::remember(
145 7
            'laravel-acl.getAllPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
146 7
            config('laravel-acl.cacheMinutes'),
147
            function() {
148 7
                return $this->cacheGetAllPermissions();
149 7
            }
150
        );
151
    }
152
153
    /**
154
     * USER & LEVEL
155
     * First try the cache to return the collection,
156
     * then fetch it from the database.
157
     *
158
     * See @cacheGetInheritedPermissions()
159
     *
160
     * @return \Illuminate\Support\Collection
161
     */
162 3 View Code Duplication
    public function getInheritedPermissions()
163
    {
164 3
        return Cache::remember(
165 3
            'laravel-acl.getInheritedPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
166 3
            config('laravel-acl.cacheMinutes'),
167
            function() {
168 3
                return $this->cacheGetInheritedPermissions();
169 3
            }
170
        );
171
    }
172
173
    /**
174
     * USER & LEVEL
175
     * First try the cache to return the collection,
176
     * then fetch it from the database.
177
     *
178
     * See @cacheGetAvailablePermissions()
179
     *
180
     * @return \Illuminate\Support\Collection
181
     */
182 2 View Code Duplication
    public function getAvailablePermissions()
183
    {
184 2
        return Cache::remember(
185 2
            'laravel-acl.getAvailablePermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id,
186 2
            config('laravel-acl.cacheMinutes'),
187
            function() {
188 2
                return $this->cacheGetAvailablePermissions();
189 2
            }
190
        );
191
    }
192
193
    /**
194
     * USER & LEVEL
195
     * First try the cache to return the collection,
196
     * then fetch it from the database.
197
     *
198
     * See @cacheHasPermissionTo()
199
     *
200
     * @param $permission
201
     * @return bool
202
     */
203 3
    public function hasPermissionTo($permission)
204
    {
205 3
        return Cache::remember(
206 3
            'laravel-acl.hasPermissionTo_' . (new ReflectionClass($this))->getShortName() . '_' . $this->id . '_Permission_' . $permission,
207 3
            config('laravel-acl.cacheMinutes'),
208
            function() use ($permission) {
209 3
                return $this->cacheHasPermissionTo($permission);
210 3
            }
211
        );
212
    }
213
214
    /**
215
     * USER & LEVEL
216
     * Returns if a level is lower ranking than another level,
217
     * or if a user is lower ranking than another user.
218
     *
219
     * @param $other
220
     * @return bool
221
     */
222 3
    public function isLowerThan($other)
223
    {
224 3
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank > $other->level->rank : $this->rank > $other->rank;
225
    }
226
227
    /**
228
     * USER & LEVEL
229
     * Returns if a level is higher ranking than another level,
230
     * or if a user is higher ranking than another user.
231
     *
232
     * @param $other
233
     * @return bool
234
     */
235 3
    public function isHigherThan($other)
236
    {
237 3
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank < $other->level->rank : $this->rank < $other->rank;
238
    }
239
240
    /**
241
     * USER & LEVEL
242
     * Returns if a equal is lower ranking than another level,
243
     * or if a user is equal ranking than another user.
244
     *
245
     * @param $other
246
     * @return bool
247
     */
248 1
    public function isEqualTo($other)
249
    {
250 1
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank == $other->level->rank : $this->rank == $other->rank;
251
    }
252
253
254
    /* ------------------------------------------------------------------------------------------------
255
     |  Other Functions
256
     | ------------------------------------------------------------------------------------------------
257
     */
258
    /**
259
     * USER & LEVEL
260
     * Return a collection of permissions
261
     * assigned to a user
262
     *
263
     * @return \Illuminate\Support\Collection
264
     */
265 2
    protected function cacheGetPermissions()
266
    {
267 2
        return $this->permissions;
0 ignored issues
show
Bug introduced by
The property permissions does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
268
    }
269
270
    /**
271
     * USER & LEVEL
272
     * Return a collection of all permissions
273
     * associated to a user by direct or
274
     * inheritance
275
     *
276
     * @return \Illuminate\Support\Collection
277
     */
278 7
    protected function cacheGetAllPermissions()
279
    {
280 7
        if(is_a($this, config('laravel-acl.level'), true)) {
281 3
            $rank = $this->rank;
282
        } else {
283 4
            $rank = $this->level ? $this->level->rank : null;
284
        }
285
286 7
        if(!$rank) {
287 1
            return $this->permissions;
288
        }
289
290 6
        $levels = config('laravel-acl.level', Level::class)::where('rank', '>=', $rank)->with('permissions')->get();
291
292 6
        $allPerms = new Collection();
293
294
        $levels->each(function($level, $key) use (&$allPerms) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
295 6
            $allPerms = $allPerms->merge($level->permissions);
296 6
        });
297
298 6
        return $allPerms->merge($this->permissions)->unique('id');
299
    }
300
301
    /**
302
     * USER & LEVEL
303
     * Return a collection of permissions that
304
     * are inherited from a higher level
305
     *
306
     * @return \Illuminate\Support\Collection
307
     */
308 3
    protected function cacheGetInheritedPermissions()
309
    {
310 3
        $rank = is_a($this, config('laravel-acl.level'), true) ? $this->rank : $this->level->rank;
311
312 3
        $levels = is_a($this, config('laravel-acl.user'), true) ?
313 3
            config('laravel-acl.level', Level::class)::where('rank', '>=', $rank)->with('permissions')->get() : config('laravel-acl.level', Level::class)::where('rank', '>', $rank)->with('permissions')->get();
314
315 3
        $inheritedPerms = new Collection();
316
317
        $levels->each(function($level, $key) use (&$inheritedPerms) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
318 3
            $inheritedPerms = $inheritedPerms->merge($level->permissions->load('level'));
319 3
        });
320
321 3
        return $inheritedPerms->unique('id');
322
    }
323
324
    /**
325
     * USER & LEVEL
326
     * Return a collection of permissions still able to be assigned
327
     * ie, not already inherited or explicitly assigned
328
     *
329
     * @return \Illuminate\Support\Collection
330
     */
331 2
    protected function cacheGetAvailablePermissions()
332
    {
333 2
        $allPerms = config('laravel-acl.permission', Permission::class)::with('level')->get();
334
335 2
        return $allPerms->diff($this->permissions);
336
    }
337
338
    /**
339
     * USER & LEVEL
340
     * Check if a model has permission to do something
341
     *
342
     * @param $permission
343
     * @return bool
344
     */
345 3
    protected function cacheHasPermissionTo($permission)
346
    {
347 3
        $permission = $this->getPermission($permission);
348
349 3
        if (is_a($this, config('laravel-acl.user'), true)) {
350 2
            $negatedPermissions = $this->permissions()->wherePivot('negated', true)->get();
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
351 2
            if ($this->getAllPermissions()->contains('id', $permission->id)) {
352 1
                return !$negatedPermissions->contains('id', $permission->id);
353
            }
354
        }
355
356 2
        return $this->getAllPermissions()->contains('id', $permission->id) || $this->getAllPermissions()->contains('name', '*');
357
    }
358
359
    /**
360
     * USER & LEVEL
361
     * Modify a given set of permissions, given the action to be taken
362
     *
363
     * @param $permissions
364
     * @param string $action
365
     */
366 24
    protected function modifyPermissions($permissions, $action)
367
    {
368 24
        Cache::forget('laravel-acl.getAvailablePermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id);
369 24
        Cache::forget('laravel-acl.getAllPermissionsFor' . (new ReflectionClass($this))->getShortName() . '_' . $this->id);
370 24
        Cache::forget('laravel-acl.getNegatedPermissionsForUser_' . $this->id);
371
372 24
        $permissionObjects = $this->buildPermissionsArray($permissions, $action);
373
374
        switch($action) {
375 23
            case 'add':
376
                // Add and negate call the same method
377 7
            case 'negate':
378 23
                $this->addOrNegate($permissionObjects, $action);
0 ignored issues
show
Unused Code introduced by
The call to UserAndLevel::addOrNegate() has too many arguments starting with $action.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
379 23
                break;
380 5
            case 'remove':
381 3
                $this->remove($permissionObjects);
382 3
                break;
383 2
            case 'sync':
384 2
                $this->sync($permissionObjects);
385
            default:
386
                // Hopefully this never happens
387
        }
388 23
    }
389
390
    /**
391
     * USER & LEVEL
392
     * Helper function to build the array
393
     * for modifyPermissions
394
     *
395
     * @param $permissions
396
     * @param $action
397
     * @return array
398
     * @throws PermissionNotFoundException
399
     */
400 24
    protected function buildPermissionsArray($permissions, $action)
401
    {
402 24
        $permissionsObjects = [];
403
404 24
        foreach ($permissions as $permission) {
405 24
            $permission = $this->getPermission($permission);
406 23
            Cache::forget('laravel-acl.hasPermissionTo_' . (new ReflectionClass($this))->getShortName() . '_' . $this->id . '_Permission_' . $permission->id);
407
408 23
            if ($action == 'negate') {
409 2
                $permissionsObjects[$permission->id] = ['negated' => true];
410
            } else {
411 23
                is_a($this, config('laravel-acl.user')) ?
412 23
                    array_push($permissionsObjects, $permission->id) : array_push($permissionsObjects, $permission);
413
            }
414
        }
415
416 23
        return $permissionsObjects;
417
    }
418
419
    /**
420
     * USER & LEVEL
421
     * Helper function to handle adding or negating permissions
422
     *
423
     * @param $permissionObjects
424
     */
425 23
    protected function addOrNegate($permissionObjects)
426
    {
427 23
        if (is_a($this, config('laravel-acl.user'))) {
428 13
            $this->permissions()->attach($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
429
        }
430
431 23
        if (is_a($this, config('laravel-acl.level'))) {
432 12
            $this->permissions()->saveMany($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
433
        }
434 23
    }
435
436
    /**
437
     * USER & LEVEL
438
     * Helper function to handle removing permissions
439
     *
440
     * @param $permissionObjects
441
     */
442 3 View Code Duplication
    protected function remove($permissionObjects)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
443
    {
444 3
        if (is_a($this, config('laravel-acl.user'))) {
445 2
            $this->permissions()->detach($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
446
        }
447
448 3
        if (is_a($this, config('laravel-acl.level'))) {
449
            config('laravel-acl.permission', Permission::class)::whereIn('id', array_map(function($permission) {
450 1
                return $permission->id;
451 1
            }, $permissionObjects))->update(['level_id' => null]);
452
        }
453 3
    }
454
455
    /**
456
     * USER & LEVEL
457
     * Help function to handle syncing permissions
458
     *
459
     * @param $permissionObjects
460
     */
461 2 View Code Duplication
    protected function sync($permissionObjects)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
462
    {
463 2
        if (is_a($this, config('laravel-acl.user'))) {
464 1
            $this->permissions()->sync($permissionObjects);
0 ignored issues
show
Bug introduced by
The method permissions() does not exist on z1haze\Acl\Traits\UserAndLevel. Did you maybe mean addPermissions()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
465
        }
466
467 2
        if (is_a($this, config('laravel-acl.level'))) {
468 1
            $this->clearPermissions();
469
470 1
            config('laravel-acl.permission', Permission::class)::whereIn('id', array_map(function($permission) {
471 1
                return $permission->id;
472 1
            }, $permissionObjects))
473 1
                ->update(['level_id' => $this->id]);
474
        }
475 2
    }
476
477
    /**
478
     * USER & LEVEL
479
     * Helper function to get the permission whether it is the permission ID
480
     * or the permission name, or the permission object itself.
481
     *
482
     * @param $permission
483
     * @return \z1haze\Acl\Models\Permission
484
     * @throws PermissionNotFoundException
485
     */
486 24 View Code Duplication
    protected function getPermission($permission)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
487
    {
488 24
        if (is_string($permission)) {
489 5
            $permission = config('laravel-acl.permission', Permission::class)::whereName($permission)->first();
490
        }
491
492 24
        if (is_int($permission)) {
493 4
            $permission = config('laravel-acl.permission', Permission::class)::find($permission);
494
        }
495
496 24
        if (!$permission) {
497 1
            throw new PermissionNotFoundException;
498
        }
499
500 23
        return $permission;
501
    }
502
}