HasPermissions::forgetCachedPermissions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Maklad\Permission\Traits;
4
5
use Illuminate\Support\Collection;
6
use Jenssegers\Mongodb\Eloquent\Builder;
7
use Jenssegers\Mongodb\Eloquent\Model;
8
use Jenssegers\Mongodb\Relations\BelongsToMany;
9
use Maklad\Permission\Contracts\PermissionInterface as Permission;
10
use Maklad\Permission\Exceptions\GuardDoesNotMatch;
11
use Maklad\Permission\Guard;
12
use Maklad\Permission\Helpers;
13
use Maklad\Permission\Models\Role;
14
use Maklad\Permission\PermissionRegistrar;
15
16
/**
17
 * Trait HasPermissions
18
 * @package Maklad\Permission\Traits
19
 */
20
trait HasPermissions
21
{
22
    private $permissionClass;
23
24 123
    public static function bootHasPermissions()
25 6
    {
26 2
        static::deleting(function (Model $model) {
27
            if (isset($model->forceDeleting) && !$model->forceDeleting) {
0 ignored issues
show
Bug introduced by
The property forceDeleting does not seem to exist on Jenssegers\Mongodb\Eloquent\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
28
                return;
29 4
            }
30 123
31 123
            $model->permissions()->sync([]);
32
        });
33
    }
34
35
    public function getPermissionClass()
36
    {
37 54
        if ($this->permissionClass === null) {
38
            $this->permissionClass = app(PermissionRegistrar::class)->getPermissionClass();
39 54
        }
40
        return $this->permissionClass;
41
    }
42
43
    /**
44
     * A role may be given various permissions.
45 2
     * @return BelongsToMany
46
     */
47 2
    public function permissions(): BelongsToMany
48
    {
49
        return $this->belongsToMany(config('permission.models.permission'));
0 ignored issues
show
Bug introduced by
It seems like belongsToMany() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
        return $this->/** @scrutinizer ignore-call */ belongsToMany(config('permission.models.permission'));
Loading history...
50
    }
51
52
    /**
53
     * Grant the given permission(s) to a role.
54
     *
55
     * @param string|array|Permission|\Illuminate\Support\Collection $permissions
56
     *
57
     * @return $this
58 46
     * @throws GuardDoesNotMatch
59
     */
60 46
    public function givePermissionTo(...$permissions): self
61 46
    {
62 46
        $permissions = collect($permissions)
63 44
            ->flatten()
64 46
            ->map(function ($permission) {
65 44
                return $this->getStoredPermission($permission);
66 42
            })
67 44
            ->each(function ($permission) {
68 41
                $this->ensureModelSharesGuard($permission);
69
            })
70 41
            ->all();
71
72 41
        $this->permissions()->saveMany($permissions);
73
74 41
        $this->forgetCachedPermissions();
75
76
        return $this;
77
    }
78
79
    /**
80
     * Remove all current permissions and set the given ones.
81
     *
82
     * @param string|array|Permission|\Illuminate\Support\Collection $permissions
83
     *
84
     * @return $this
85 4
     * @throws GuardDoesNotMatch
86
     */
87 4
    public function syncPermissions(...$permissions): self
88
    {
89 4
        $this->permissions()->sync([]);
90
91
        return $this->givePermissionTo($permissions);
92
    }
93
94
    /**
95
     * Revoke the given permission.
96
     *
97
     * @param string|array|Permission|\Illuminate\Support\Collection $permissions
98
     *
99
     * @return $this
100 6
     * @throws \Maklad\Permission\Exceptions\GuardDoesNotMatch
101
     */
102 6
    public function revokePermissionTo(...$permissions): self
103 6
    {
104 6
        collect($permissions)
105 6
            ->flatten()
106 6
            ->map(function ($permission) {
107
                $permission = $this->getStoredPermission($permission);
108 6
                $this->permissions()->detach($permission);
109 6
110
                return $permission;
111 6
            });
112
113 6
        $this->forgetCachedPermissions();
114
115
        return $this;
116
    }
117
118
    /**
119
     * @param string|Permission $permission
120
     *
121
     * @return Permission
122 45
     * @throws \ReflectionException
123
     */
124 45
    protected function getStoredPermission($permission): Permission
125 32
    {
126
        if (\is_string($permission)) {
127
            return $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName());
128 16
        }
129
130
        return $permission;
131
    }
132
133
    /**
134
     * @param Model $roleOrPermission
135
     *
136
     * @throws GuardDoesNotMatch
137 83
     * @throws \ReflectionException
138
     */
139 83
    protected function ensureModelSharesGuard(Model $roleOrPermission)
140 5
    {
141 5
        if (! $this->getGuardNames()->contains($roleOrPermission->guard_name)) {
0 ignored issues
show
Bug introduced by
The property guard_name does not exist on Jenssegers\Mongodb\Eloquent\Model. Did you mean guarded?
Loading history...
142 5
            $expected = $this->getGuardNames();
143
            $given    = $roleOrPermission->guard_name;
144 5
            $helpers  = new Helpers();
145
146 79
            throw new GuardDoesNotMatch($helpers->getGuardDoesNotMatchMessage($expected, $given));
147
        }
148
    }
149
150
    /**
151
     * @return Collection
152 86
     * @throws \ReflectionException
153
     */
154 86
    protected function getGuardNames(): Collection
155
    {
156
        return (new Guard())->getNames($this);
157
    }
158
159
    /**
160
     * @return string
161 78
     * @throws \ReflectionException
162
     */
163 78
    protected function getDefaultGuardName(): string
164
    {
165
        return (new Guard())->getDefaultName($this);
166
    }
167
168
    /**
169 80
     * Forget the cached permissions.
170
     */
171 80
    public function forgetCachedPermissions()
172 80
    {
173
        app(PermissionRegistrar::class)->forgetCachedPermissions();
174
    }
175
176
    /**
177
     * Convert to Permission Models
178
     *
179
     * @param string|array|Collection $permissions
180
     *
181 7
     * @return Collection
182
     */
183 7
    private function convertToPermissionModels($permissions): Collection
184 3
    {
185
        if (\is_array($permissions)) {
186
            $permissions = collect($permissions);
187 7
        }
188 5
189
        if (! $permissions instanceof Collection) {
190
            $permissions = collect([$permissions]);
191 7
        }
192 7
193 7
        $permissions = $permissions->map(function ($permission) {
194
            return $this->getStoredPermission($permission);
195 6
        });
196
197
        return $permissions;
198
    }
199
200
    /**
201
     * Return a collection of permission names associated with this user.
202
     *
203 1
     * @return Collection
204
     */
205 1
    public function getPermissionNames(): Collection
206
    {
207
        return $this->getAllPermissions()->pluck('name');
208
    }
209
210
    /**
211 3
     * Return all the permissions the model has via roles.
212
     */
213 3
    public function getPermissionsViaRoles(): Collection
214 3
    {
215 2
        return $this->load('roles', 'roles.permissions')
0 ignored issues
show
Bug introduced by
It seems like load() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

215
        return $this->/** @scrutinizer ignore-call */ load('roles', 'roles.permissions')
Loading history...
216 3
            ->roles->flatMap(function (Role $role) {
217
                return $role->permissions;
0 ignored issues
show
Bug Best Practice introduced by
The property permissions does not exist on Maklad\Permission\Models\Role. Since you implemented __get, consider adding a @property annotation.
Loading history...
218
            })->sort()->values();
219
    }
220
221
    /**
222 2
     * Return all the permissions the model has, both directly and via roles.
223
     */
224 2
    public function getAllPermissions(): Collection
225 2
    {
226 2
        return $this->permissions
0 ignored issues
show
Bug introduced by
The property permissions does not exist on Maklad\Permission\Traits\HasPermissions. Did you mean permissionClass?
Loading history...
227 2
            ->merge($this->getPermissionsViaRoles())
228
            ->sort()
229
            ->values();
230
    }
231
232
    /**
233
     * Determine if the model may perform the given permission.
234
     *
235
     * @param string|Permission $permission
236
     * @param string|null $guardName
237
     *
238
     * @return bool
239 24
     * @throws \ReflectionException
240
     */
241 24
    public function hasPermissionTo($permission, $guardName = null): bool
242 15
    {
243 15
        if (\is_string($permission)) {
244 15
            $permission = $this->getPermissionClass()->findByName(
245
                $permission,
246
                $guardName ?? $this->getDefaultGuardName()
247
            );
248 22
        }
249
250
        return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
251
    }
252
253
    /**
254
     * Determine if the model has any of the given permissions.
255
     *
256
     * @param array ...$permissions
257
     *
258
     * @return bool
259 8
     * @throws \ReflectionException
260
     */
261 8
    public function hasAnyPermission(...$permissions): bool
262 6
    {
263
        if (\is_array($permissions[0])) {
264
            $permissions = $permissions[0];
265 8
        }
266 8
267 8
        foreach ($permissions as $permission) {
268
            if ($this->hasPermissionTo($permission)) {
269
                return true;
270
            }
271 5
        }
272
273
        return false;
274
    }
275
276
    /**
277
     * Determine if the model has all of the given permissions(s).
278
     *
279
     * @param $permissions
280
     *
281 17
     * @return bool
282
     * @throws \ReflectionException
283 17
     */
284
    public function hasAllPermissions(...$permissions): bool
285
    {
286
        $helpers = new Helpers();
287
        $permissions = $helpers->flattenArray($permissions);
288
289
        foreach ($permissions as $permission) {
290
            if (!$this->hasPermissionTo($permission)) {
291
                return false;
292
            }
293
        }
294 23
        return true;
295
    }
296 23
297 1
    /**
298
     * Determine if the model has, via roles, the given permission.
299
     *
300 23
     * @param Permission $permission
301
     *
302
     * @return bool
303
     */
304
    protected function hasPermissionViaRole(Permission $permission): bool
305
    {
306 1
        return $this->hasRole($permission->roles);
0 ignored issues
show
Bug introduced by
Accessing roles on the interface Maklad\Permission\Contracts\PermissionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
Bug introduced by
It seems like hasRole() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

306
        return $this->/** @scrutinizer ignore-call */ hasRole($permission->roles);
Loading history...
307
    }
308 1
309
    /**
310
     * Determine if the model has the given permission.
311
     *
312
     * @param string|Permission $permission
313
     *
314
     * @return bool
315
     * @throws \ReflectionException
316
     */
317
    public function hasDirectPermission($permission): bool
318
    {
319 7
        if (\is_string($permission)) {
320
            $permission = $this->getPermissionClass()->findByName($permission, $this->getDefaultGuardName());
321 7
        }
322
323 6
        return $this->permissions->contains('id', $permission->id);
0 ignored issues
show
Bug introduced by
The property permissions does not exist on Maklad\Permission\Traits\HasPermissions. Did you mean permissionClass?
Loading history...
324
    }
325 6
326 6
    /**
327
     * Return all permissions the directory coupled to the model.
328 6
     */
329
    public function getDirectPermissions(): Collection
330 6
    {
331 6
        return $this->permissions;
0 ignored issues
show
Bug introduced by
The property permissions does not exist on Maklad\Permission\Traits\HasPermissions. Did you mean permissionClass?
Loading history...
332
    }
333
334
    /**
335
     * Scope the model query to certain permissions only.
336
     *
337
     * @param Builder $query
338
     * @param string|array|\Maklad\Permission\Contracts\PermissionInterface|Collection $permissions
339
     *
340
     * @return Builder
341
     */
342
    public function scopePermission(Builder $query, $permissions): Builder
343
    {
344
        $permissions = $this->convertToPermissionModels($permissions);
0 ignored issues
show
Bug introduced by
It seems like $permissions can also be of type Maklad\Permission\Contracts\PermissionInterface; however, parameter $permissions of Maklad\Permission\Traits...ertToPermissionModels() does only seem to accept Illuminate\Support\Collection|array|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

344
        $permissions = $this->convertToPermissionModels(/** @scrutinizer ignore-type */ $permissions);
Loading history...
345
346
        $roles = \collect([]);
347
348
        foreach ($permissions as $permission) {
349
            $roles = $roles->merge($permission->roles);
350
        }
351
        $roles = $roles->unique();
352
353
        return $query->orWhereIn('permission_ids', $permissions->pluck('_id'))
354
            ->orWhereIn('role_ids', $roles->pluck('_id'));
355
    }
356
}
357