Completed
Push — master ( dcb410...e7354d )
by Stephen
07:30
created

UserAndLevel::modifyPermissions()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 22
ccs 0
cts 17
cp 0
rs 8.6737
cc 6
eloc 12
nc 16
nop 2
crap 42
1
<?php
2
3
namespace z1haze\Acl\Traits;
4
5
use Illuminate\Support\Collection;
6
use z1haze\Acl\Exceptions\PermissionNotFoundException;
7
use z1haze\Acl\Models\Level;
8
use z1haze\Acl\Models\Permission;
9
10
/**
11
 * Class UserAndLevel
12
 * @package z1haze\Acl\Traits
13
 */
14
trait UserAndLevel
15
{
16
    /**
17
     * USER & LEVEL
18
     * Check if a model has a given level
19
     *
20
     * @param $level
21
     * @return bool
22
     */
23
    public function isLevel($level)
24
    {
25
        $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...
26
27
        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...
28
    }
29
30
    /**
31
     * USER & LEVEL
32
     * Check if a user has or has inherited
33
     * a given level
34
     *
35
     * @param $level
36
     * @return bool
37
     */
38
    public function hasLevel($level)
39
    {
40
        $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...
41
42
        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...
43
    }
44
45
    /**
46
     * USER & LEVEL
47
     * Add a single permission to a model
48
     *
49
     * @param $permission
50
     */
51
    public function addPermission($permission)
52
    {
53
        $this->modifyPermissions([$permission], 'add');
54
    }
55
56
    /**
57
     * USER & LEVEL
58
     * Add an array of permissions to a model
59
     *
60
     * @param $permissions
61
     */
62
    public function addPermissions(array $permissions)
63
    {
64
        $this->modifyPermissions($permissions, 'add');
65
    }
66
67
    /**
68
     * USER & LEVEL
69
     * Remove a permission from a model
70
     *
71
     * @param $permission
72
     */
73
    public function removePermission($permission)
74
    {
75
        $this->modifyPermissions([$permission], 'remove');
76
    }
77
78
    /**
79
     * USER & LEVEL
80
     * Remove an array of permissions objects or permission names (can be mixed)
81
     *
82
     * @param $permissions
83
     */
84
    public function removePermissions(array $permissions)
85
    {
86
        $this->modifyPermissions($permissions, 'remove');
87
    }
88
89
    /**
90
     * USER & LEVEL
91
     * Sync permissions on a model
92
     *
93
     * @param $permissions
94
     */
95
    public function syncPermissions(array $permissions)
96
    {
97
        $this->modifyPermissions($permissions, 'sync');
98
    }
99
100
    /**
101
     * USER & LEVEL
102
     * Clear permissions from a model
103
     *
104
     * @return mixed
105
     */
106
    public function clearPermissions()
107
    {
108
        if (is_a($this, config('laravel-acl.user'), true))
109
            $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...
110
111
        if (is_a($this, config('laravel-acl.level'), true))
112
            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...
113
    }
114
115
116
    /* ------------------------------------------------------------------------------------------------
117
     |  Other Functions
118
     | ------------------------------------------------------------------------------------------------
119
     */
120
    /**
121
     * USER & LEVEL
122
     * First try the cache to return the collection,
123
     * then fetch it from the database.
124
     *
125
     * See @cacheGetAllPermissions()
126
     *
127
     * @return mixed
128
     */
129 View Code Duplication
    public function getAllPermissions()
1 ignored issue
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...
130
    {
131
        $model = is_a($this, config('laravel-acl.level'), true) ? 'Level' : 'User';
132
133
        return \Cache::remember(
134
            'laravel-acl.getAllPermissionsFor' . $model . '_' . $this->id,
135
            config('laravel-acl.cacheMinutes'),
136
            function () {
137
                return $this->cacheGetAllPermissions();
138
            }
139
        );
140
    }
141
142
    /**
143
     * USER & LEVEL
144
     * First try the cache to return the collection,
145
     * then fetch it from the database.
146
     *
147
     * See @cacheGetInheritedPermissions()
148
     *
149
     * @return mixed
150
     */
151 View Code Duplication
    public function getInheritedPermissions()
1 ignored issue
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...
152
    {
153
        $model = is_a($this, config('laravel-acl.level'), true) ? 'Level' : 'User';
154
155
        return \Cache::remember(
156
            'laravel-acl.getInheritedPermissionsFor' . $model . '_' . $this->id,
157
            config('laravel-acl.cacheMinutes'),
158
            function () {
159
                return $this->cacheGetInheritedPermissions();
160
            }
161
        );
162
    }
163
164
    /**
165
     * USER & LEVEL
166
     * First try the cache to return the collection,
167
     * then fetch it from the database.
168
     *
169
     * See @cacheGetAvailablePermissions()
170
     *
171
     * @return mixed
172
     */
173 View Code Duplication
    public function getAvailablePermissions()
1 ignored issue
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...
174
    {
175
        $model = is_a($this, config('laravel-acl.level'), true) ? 'Level' : 'User';
176
177
        return \Cache::remember(
178
            'laravel-acl.getAvailablePermissionsFor' . $model . '_' . $this->id,
179
            config('laravel-acl.cacheMinutes'),
180
            function () {
181
                return $this->cacheGetAvailablePermissions();
182
            }
183
        );
184
    }
185
186
    /**
187
     * USER & LEVEL
188
     * First try the cache to return the collection,
189
     * then fetch it from the database.
190
     *
191
     * See @cacheHasPermissionTo()
192
     *
193
     * @param $permission
194
     * @return mixed
195
     */
196
    public function hasPermissionTo($permission)
197
    {
198
        $model = is_a($this, config('laravel-acl.level'), true) ? 'Level' : 'User';
199
200
        return \Cache::remember(
201
            'laravel-acl.hasPermissionTo_' . $model . '_' . $this->id . '_Permission_' . $permission,
202
            config('laravel-acl.cacheMinutes'),
203
            function () use ($permission) {
204
                return $this->cacheHasPermissionTo($permission);
205
            }
206
        );
207
    }
208
209
    /**
210
     * USER & LEVEL
211
     * Returns if a level is lower ranking than another level,
212
     * or if a user is lower ranking than another user.
213
     *
214
     * @param $other
215
     * @return bool
216
     */
217
    public function isLowerThan($other)
218
    {
219
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank > $other->level->rank : $this->rank > $other->rank;
220
    }
221
222
    /**
223
     * USER & LEVEL
224
     * Returns if a level is higher ranking than another level,
225
     * or if a user is higher ranking than another user.
226
     *
227
     * @param $other
228
     * @return bool
229
     */
230
    public function isHigherThan($other)
231
    {
232
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank < $other->level->rank : $this->rank < $other->rank;
233
    }
234
235
    /**
236
     * USER & LEVEL
237
     * Returns if a equal is lower ranking than another level,
238
     * or if a user is equal ranking than another user.
239
     *
240
     * @param $other
241
     * @return bool
242
     */
243
    public function isEqualTo($other)
244
    {
245
        return is_a($this, config('laravel-acl.user'), true) ? $this->level->rank == $other->level->rank : $this->rank == $other->rank;
246
    }
247
248
249
    /* ------------------------------------------------------------------------------------------------
250
     |  Other Functions
251
     | ------------------------------------------------------------------------------------------------
252
     */
253
    /**
254
     * USER & LEVEL
255
     * Return a collection of all permissions
256
     * associated to a user by direct or
257
     * inheritance
258
     *
259
     * @return mixed
260
     */
261
    protected function cacheGetAllPermissions()
262
    {
263
        $rank = is_a($this, config('laravel-acl.level'), true) ? $this->rank : $this->level->rank;
264
265
        $levels = config('laravel-acl.level', Level::class)::where('rank', '>=', $rank)->with('permissions')->get();
266
267
        $allPerms = new Collection();
268
269
        $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...
270
            $allPerms = $allPerms->merge($level->permissions);
271
        });
272
273
        return $allPerms->merge($this->permissions)->unique('id');
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...
274
    }
275
276
    /**
277
     * USER & LEVEL
278
     * Return a collection of permissions that
279
     * are inherited from a higher level
280
     * @return mixed
281
     */
282
    protected function cacheGetInheritedPermissions()
283
    {
284
        $rank = is_a($this, config('laravel-acl.level'), true) ? $this->rank : $this->level->rank;
285
286
        $levels = is_a($this, config('laravel-acl.user'), true) ? config('laravel-acl.level', Level::class)::where('rank', '>=', $rank)->with('permissions')->get() : config('laravel-acl.level', Level::class)::where('rank', '>', $rank)->with('permissions')->get();
287
288
        $inheritedPerms = new Collection();
289
290
        $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...
291
            $inheritedPerms = $inheritedPerms->merge($level->permissions->load('level'));
292
        });
293
294
        return $inheritedPerms->unique('id');
295
    }
296
297
    /**
298
     * USER & LEVEL
299
     * Return a collection of permissions still able to be assigned
300
     * ie, not already inherited or explicitly assigned
301
     * @return \Illuminate\Database\Eloquent\Collection
302
     */
303
    protected function cacheGetAvailablePermissions()
304
    {
305
        $allPerms = config('laravel-acl.permission', Permission::class)::with('level')->get();
306
307
        return $allPerms->diff($this->permissions);
308
    }
309
310
    /**
311
     * USER & LEVEL
312
     * Check if a model has permission to do something
313
     *
314
     * @param $permission
315
     * @return bool
316
     */
317
    protected function cacheHasPermissionTo($permission)
318
    {
319
        $permission = $this->getPermission($permission);
320
321
        if (is_a($this, config('laravel-acl.user'), true)) {
322
            $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...
323
324
            if ($this->getAllPermissions()->contains('id', $permission->id)) {
325
                return !$negatedPermissions->contains('id', $permission->id);
326
            }
327
        }
328
        
329
        return $this->getAllPermissions()->contains('id', $permission->id) ||
330
        $this->getAllPermissions()->contains('name', '*');
331
    }
332
333
    /**
334
     * USER & LEVEL
335
     * Modify a given set of permissions, given the action to be taken
336
     *
337
     * @param $permissions
338
     * @param $action
339
     * @return mixed
340
     */
341
    protected function modifyPermissions($permissions, $action)
342
    {
343
        $model = is_a($this, config('laravel-acl.level'), true) ? 'Level' : 'User';
344
345
        \Cache::forget('laravel-acl.getAvailablePermissionsFor' . $model . '_' . $this->id);
346
        \Cache::forget('laravel-acl.getAllPermissionsFor' . $model . '_' . $this->id);
347
        \Cache::forget('laravel-acl.getNegatedPermissionsForUser_' . $this->id);
348
349
        $permissionObjects = $this->buildPermissionsArray($permissions, $action, $model);
350
351
        if ($action == 'add' || $action == 'negate') {
352
            $this->addOrNegate($model, $permissionObjects, $action);
353
        }
354
355
        if ($action == 'remove') {
356
            $this->remove($model, $permissionObjects);
357
        }
358
359
        if ($action == 'sync') {
360
            $this->sync($model, $permissionObjects);
361
        }
362
    }
363
364
    /**
365
     * USER & LEVEL
366
     * Helper function to build the array
367
     * for modifyPermissions
368
     *
369
     * @param $permissions
370
     * @param $action
371
     * @param $model
372
     * @return mixed
373
     * @throws PermissionNotFoundException
374
     */
375
    protected function buildPermissionsArray($permissions, $action, $model)
376
    {
377
        $permissionsObjects = [];
378
379
        foreach ($permissions as $permission) {
380
            $permission = $this->getPermission($permission);
381
            \Cache::forget('laravel-acl.hasPermissionTo_' . $model . '_' . $this->id . '_Permission_' . $permission->id);
382
383
            if ($action == 'negate') {
384
                $permissionsObjects[$permission->id] = ['negated' => true];
385
            } else {
386
                $model == 'User' ?
387
                    array_push($permissionsObjects, $permission->id) :
388
                    array_push($permissionsObjects, $permission);
389
            }
390
        }
391
392
        return $permissionsObjects;
393
    }
394
395
    /**
396
     * USER & LEVEL
397
     * Helper function to handle adding or negating permissions
398
     *
399
     * @param $model
400
     * @param $permissionObjects
401
     */
402
    protected function addOrNegate($model, $permissionObjects, $action)
0 ignored issues
show
Unused Code introduced by
The parameter $action 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...
403
    {
404
        if ($model == 'User')
405
            $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...
406
407
        if ($model == 'Level')
408
            $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...
409
    }
410
411
    /**
412
     * USER & LEVEL
413
     * Helper function to handle removing permissions
414
     *
415
     * @param $model
416
     * @param $permissionObjects
417
     */
418 View Code Duplication
    protected function remove($model, $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...
419
    {
420
        if ($model == 'User')
421
            $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...
422
423
        if ($model == 'Level') {
424
            config('laravel-acl.permission', Permission::class)::whereIn('id', array_map(function ($permission) {
425
                return $permission->id;
426
            }, $permissionObjects))->update(['level_id' => null]);
427
        }
428
    }
429
430
    /**
431
     * USER & LEVEL
432
     * Help function to handle syncing permissions
433
     *
434
     * @param $model
435
     * @param $permissionObjects
436
     */
437 View Code Duplication
    protected function sync($model, $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...
438
    {
439
        if ($model == 'User')
440
            $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...
441
442
        if ($model == 'Level') {
443
            $this->clearPermissions();
444
445
            config('laravel-acl.permission', Permission::class)::whereIn('id', array_map(function ($permission) {
446
                return $permission->id;
447
            }, $permissionObjects))
448
                ->update(['level_id' => $this->id]);
449
        }
450
    }
451
452
    /**
453
     * USER & LEVEL
454
     * Helper function to get the permission whether it is the permission ID
455
     * or the permission name, or the permission object itself.
456
     *
457
     * @param $permission
458
     * @return mixed
459
     * @throws PermissionNotFoundException
460
     */
461 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...
462
    {
463
        if (is_string($permission))
464
            $permission = config('laravel-acl.permission', Permission::class)::whereName($permission)->first();
465
466
        if (is_int($permission))
467
            $permission = config('laravel-acl.permission', Permission::class)::find($permission);
468
469
        if (!$permission)
470
            throw new PermissionNotFoundException;
471
472
        return $permission;
473
    }
474
}