Passed
Push — master ( 0f9eb8...340202 )
by Jonathan
24:22
created

User::getAllowedGroupUuids()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 0
1
<?php
2
3
namespace Uccello\Core\Models;
4
5
use Illuminate\Notifications\Notifiable;
6
use Illuminate\Foundation\Auth\User as Authenticatable;
7
use Illuminate\Database\Eloquent\SoftDeletes;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Facades\Cache;
10
use Spatie\Searchable\Searchable;
0 ignored issues
show
Bug introduced by
The type Spatie\Searchable\Searchable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Spatie\Searchable\SearchResult;
0 ignored issues
show
Bug introduced by
The type Spatie\Searchable\SearchResult was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Uccello\Core\Support\Traits\RelatedlistTrait;
13
use Uccello\Core\Support\Traits\UccelloModule;
14
use Uccello\Core\Models\Group;
15
16
class User extends Authenticatable implements Searchable
17
{
18
    use SoftDeletes;
19
    use Notifiable;
20
    use RelatedlistTrait;
21
    use UccelloModule;
22
23
    /**
24
     * The table associated with the model.
25
     *
26
     * @var string
27
     */
28
    protected $table = 'users';
29
30
    /**
31
     * The attributes that should be mutated to dates.
32
     *
33
     * @var array
34
     */
35
    protected $dates = [ 'deleted_at' ];
36
37
    /**
38
     * The attributes that should be casted to native types.
39
     *
40
     * @var array
41
     */
42
    protected $casts = [
43
        'avatar' => 'object',
44
    ];
45
46
    /**
47
     * The attributes that are mass assignable.
48
     *
49
     * @var array
50
     */
51
    protected $fillable = [
52
        'username',
53
        'name',
54
        'email',
55
        'password',
56
        'is_admin',
57
        'domain_id'
58
    ];
59
60
    /**
61
     * The attributes that should be hidden for arrays.
62
     *
63
     * @var array
64
     */
65
    protected $hidden = [
66
        'password', 'remember_token',
67
    ];
68
69
    /**
70
     * The accessors to append to the model's array form.
71
     *
72
     * @var array
73
     */
74
    protected $appends = [
75
        'recordLabel'
76
    ];
77
78
    public $searchableType = 'user';
79
80
    public $searchableColumns = [
81
        'name'
82
    ];
83
84
    public function getSearchResult(): SearchResult
85
    {
86
        return new SearchResult(
87
            $this,
88
            $this->recordLabel
89
        );
90
    }
91
92
    public function domain()
93
    {
94
        return $this->belongsTo(Domain::class);
95
    }
96
97
    public function lastDomain()
98
    {
99
        return $this->belongsTo(Domain::class);
100
    }
101
102
    public function privileges()
103
    {
104
        return $this->hasMany(Privilege::class);
105
    }
106
107
    public function menus()
108
    {
109
        return $this->hasMany(Menu::class);
110
    }
111
112
    public function groups()
113
    {
114
        return $this->belongsToMany(Group::class, 'uccello_rl_groups_users');
115
    }
116
117
    /**
118
     * Returns record label
119
     *
120
     * @return string
121
     */
122
    public function getRecordLabelAttribute() : string
123
    {
124
        return trim($this->name) ?? $this->username;
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on Uccello\Core\Models\User. 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...
125
    }
126
127
    /**
128
     * Get avatar type
129
     *
130
     * @return string
131
     */
132
    public function getAvatarTypeAttribute() : string
133
    {
134
        return $this->avatar->type ?? 'initials';
0 ignored issues
show
Bug introduced by
The property type does not exist on string.
Loading history...
135
    }
136
137
    /**
138
     * Returns initals generated from the user name
139
     *
140
     * @return string
141
     */
142
    public function getInitialsAttribute() : string
143
    {
144
        $initials = "";
145
146
        $words = explode(" ", strtoupper($this->name));
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on Uccello\Core\Models\User. 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...
147
148
        $i = 0;
149
        foreach ($words as $w) {
150
            $initials .= $w[0];
151
            $i++;
152
153
            if ($i === 3) { // Maximum: 3 letters
154
                break;
155
            }
156
        }
157
158
        return $initials;
159
    }
160
161
    /**
162
     * Returns the image to use as the user avatar
163
     *
164
     * @return string
165
     */
166
    public function getImageAttribute() : string
167
    {
168
        $image = 'vendor/uccello/uccello/images/user-no-image.png';
169
170
        if ($this->avatarType === 'gravatar') {
171
            $image = 'https://www.gravatar.com/avatar/' . md5($this->email) . '?d=mm';
0 ignored issues
show
Bug introduced by
The property email does not seem to exist on Uccello\Core\Models\User. 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...
172
173
        } elseif ($this->avatarType === 'image' && !empty($this->avatar->path)) {
0 ignored issues
show
Bug introduced by
The property path does not exist on string.
Loading history...
174
            $image = $this->avatar->path;
175
        }
176
177
        return $image;
178
    }
179
180
    /**
181
     * Returns user's roles on a domain
182
     *
183
     * @param \Uccello\Core\Models\Domain $domain
184
     * @return \Illuminate\Support\Collection
185
     */
186
    public function rolesOnDomain($domain) : Collection
187
    {
188
        return Cache::remember('domain_'.$domain->slug.'_roles', 600, function () use($domain) {
189
            $roles = collect();
190
191
            if (config('uccello.roles.display_ancestors_roles')) {
192
                $treeDomainsIds = $domain->findAncestors()->pluck('id');
193
            } else {
194
                $treeDomainsIds = collect([ $domain->id ]);
195
            }
196
197
            foreach ($treeDomainsIds as $treeDomainId) {
198
                $_domain = Domain::find($treeDomainId);
199
                foreach ($this->privileges->where('domain_id', $_domain->id) as $privilege) {
200
                    $roles[ ] = $privilege->role;
201
                }
202
            }
203
204
            return $roles;
205
        });
206
207
    }
208
209
    /**
210
     * Check if the user has at least a role on a domain
211
     *
212
     * @param \Uccello\Core\Models\Domain $domain
213
     * @return boolean
214
     */
215
    public function hasRoleOnDomain($domain) : bool {
216
        if ($this->is_admin) {
217
            return true;
218
        }
219
220
        return $this->rolesOnDomain($domain)->count() > 0;
221
    }
222
223
    /**
224
     * Check if the user has at least a role on a domain or its descendants
225
     *
226
     * @param \Uccello\Core\Models\Domain $domain
227
     * @return boolean
228
     */
229
    public function hasRoleOnDescendantDomain(Domain $domain) : bool {
230
        if ($this->is_admin) {
231
            return true;
232
        }
233
234
        $hasRole = false;
235
236
        $descendants = Cache::remember('domain_'.$domain->slug.'_descendants', 600, function () use($domain) {
237
            return $domain->findDescendants()->get();
238
        });
239
240
        foreach ($descendants as $descendant) {
241
            if ($this->hasRoleOnDomain($descendant)) {
242
                $hasRole = true;
243
                break;
244
            }
245
        }
246
247
        return $hasRole;
248
    }
249
250
    /**
251
     * Returns all user capabilities on a module in a domain.
252
     * If the user has a capability in one of the parents of a domain, he also has it in that domain.
253
     *
254
     * @param \Uccello\Core\Models\Domain $domain
255
     * @param \Uccello\Core\Models\Module $module
256
     * @return \Illuminate\Support\Collection
257
     */
258
    public function capabilitiesOnModule(Domain $domain, Module $module) : Collection
259
    {
260
        $keyName = 'user_'.$this->id.'_'.$domain->slug.'_'.$module->name.'_capabilities';
0 ignored issues
show
Bug introduced by
The property name does not seem to exist on Uccello\Core\Models\Module. 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...
261
262
        return Cache::remember($keyName, 600, function () use($domain, $module) {
263
            $capabilities = collect();
264
265
            // Get the domain and all its parents
266
            $domainParents = $domain->findAncestors()->get();
267
268
            // Get user privileges on each domain
269
            foreach ($domainParents as $_domain) {
270
                $privileges = $this->privileges->where('domain_id', $_domain->id);
271
272
                foreach ($privileges as $privilege) {
273
274
                    foreach ($privilege->role->profiles as $profile) {
275
                        $capabilities = $capabilities->merge($profile->capabilitiesOnModule($module));
276
                    }
277
                }
278
            }
279
280
            return $capabilities;
281
        });
282
    }
283
284
    /**
285
     * Checks if the user has a capability on a module in a domain.
286
     *
287
     * @param string $capabilityName
288
     * @param \Uccello\Core\Models\Domain $domain
289
     * @param \Uccello\Core\Models\Module $module
290
     * @return boolean
291
     */
292
    public function hasCapabilityOnModule(string $capabilityName, Domain $domain, Module $module) : bool
293
    {
294
        $capability = capability($capabilityName);
295
296
        $userCapabilities = $this->capabilitiesOnModule($domain, $module);
297
298
        return $this->is_admin || $userCapabilities->contains($capability);
299
    }
300
301
    /**
302
     * Checks if the user can access to settings panel.
303
     * Checks if the user has at least one admin capability on admin modules in a domain.
304
     *
305
     * @param \Uccello\Core\Models\Domain|null $domain
306
     * @return boolean
307
     */
308
    public function canAccessToSettingsPanel(?Domain $domain) : bool
309
    {
310
        if (empty($domain)) {
311
            $domain = Domain::first();
312
        }
313
314
        $keyName = 'user_'.$this->id.'_'.$domain->slug.'_can_access_to_settings_panel';
315
316
        return Cache::remember($keyName, 600, function () use($domain) {
317
318
            $hasCapability = false;
319
320
            foreach (Module::all() as $module) {
321
                if ($module->isAdminModule() === true && $this->canAdmin($domain, $module)) {
322
                    $hasCapability = true;
323
                    break;
324
                }
325
            }
326
327
            return $hasCapability;
328
        });
329
    }
330
331
    /**
332
     * Checks if the user can admin a module in a domain.
333
     *
334
     * @param \Uccello\Core\Models\Domain $domain
335
     * @param \Uccello\Core\Models\Module $module
336
     * @return boolean
337
     */
338
    public function canAdmin(Domain $domain, Module $module) : bool
339
    {
340
        return $this->hasCapabilityOnModule('admin', $domain, $module);
341
    }
342
343
    /**
344
     * Checks if the user can create a module in a domain.
345
     *
346
     * @param \Uccello\Core\Models\Domain $domain
347
     * @param \Uccello\Core\Models\Module $module
348
     * @return boolean
349
     */
350
    public function canCreate(Domain $domain, Module $module) : bool
351
    {
352
        return $this->hasCapabilityOnModule('create', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
353
    }
354
355
    /**
356
     * Checks if the user can retrieve a module in a domain.
357
     *
358
     * @param \Uccello\Core\Models\Domain $domain
359
     * @param \Uccello\Core\Models\Module $module
360
     * @return boolean
361
     */
362
    public function canRetrieve(Domain $domain, Module $module) : bool
363
    {
364
        return $this->hasCapabilityOnModule('retrieve', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
365
    }
366
367
    /**
368
     * Checks if the user can update a module in a domain.
369
     *
370
     * @param \Uccello\Core\Models\Domain $domain
371
     * @param \Uccello\Core\Models\Module $module
372
     * @return boolean
373
     */
374
    public function canUpdate(Domain $domain, Module $module) : bool
375
    {
376
        return $this->hasCapabilityOnModule('update', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
377
    }
378
379
    /**
380
     * Checks if the user can delete a module in a domain.
381
     *
382
     * @param \Uccello\Core\Models\Domain $domain
383
     * @param \Uccello\Core\Models\Module $module
384
     * @return boolean
385
     */
386
    public function canDelete(Domain $domain, Module $module) : bool
387
    {
388
        return $this->hasCapabilityOnModule('delete', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
389
    }
390
391
    /**
392
     * Checks if the user can create by API a module in a domain.
393
     *
394
     * @param \Uccello\Core\Models\Domain $domain
395
     * @param \Uccello\Core\Models\Module $module
396
     * @return boolean
397
     */
398
    public function canCreateByApi(Domain $domain, Module $module) : bool
399
    {
400
        return $this->hasCapabilityOnModule('api-create', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
401
    }
402
403
    /**
404
     * Checks if the user can retrieve by API a module in a domain.
405
     *
406
     * @param \Uccello\Core\Models\Domain $domain
407
     * @param \Uccello\Core\Models\Module $module
408
     * @return boolean
409
     */
410
    public function canRetrieveByApi(Domain $domain, Module $module) : bool
411
    {
412
        return $this->hasCapabilityOnModule('api-retrieve', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
413
    }
414
415
    /**
416
     * Checks if the user can update by API a module in a domain.
417
     *
418
     * @param \Uccello\Core\Models\Domain $domain
419
     * @param \Uccello\Core\Models\Module $module
420
     * @return boolean
421
     */
422
    public function canUpdateByApi(Domain $domain, Module $module) : bool
423
    {
424
        return $this->hasCapabilityOnModule('api-update', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
425
    }
426
427
    /**
428
     * Checks if the user can delete by API a module in a domain.
429
     *
430
     * @param \Uccello\Core\Models\Domain $domain
431
     * @param \Uccello\Core\Models\Module $module
432
     * @return boolean
433
     */
434
    public function canDeleteByApi(Domain $domain, Module $module) : bool
435
    {
436
        return $this->hasCapabilityOnModule('api-delete', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
437
    }
438
439
    /**
440
     * Checks if the user has almost a role allowing to view data transversally
441
     *
442
     * @param \Uccello\Core\Models\Domain $domain
443
     * @return boolean
444
     */
445
    public function canSeeDescendantsRecords(Domain $domain) : bool
446
    {
447
        $allowed = false;
448
449
        if ($this->is_admin) {
450
            $allowed = true;
451
        } else {
452
            $roles = $this->rolesOnDomain($domain);
453
            foreach ($roles as $role) {
454
                if ($role->see_descendants_records) {
455
                    $allowed = true;
456
                    break;
457
                }
458
            }
459
        }
460
461
        return $allowed;
462
    }
463
464
    public function getAllowedGroupUuids()
465
    {
466
        // Use cache
467
        $allowedGroups = Cache::rememberForever(
468
            'allowed_groups_for_' . ($this->is_admin ? 'admin' : $this->getKey()),
469
            function () {
470
                return $this->getAllowedGroupUuidsProcess();
471
            }
472
        );
473
474
        return $allowedGroups;
475
    }
476
477
    public function getAllowedGroupsAndUsers($addUsers = true)
478
    {
479
        // Use cache
480
        $allowedGroupsAndUsers = Cache::rememberForever(
481
            'allowed_group_users_for_' . ($addUsers ? 'u_' : '') . ($this->is_admin ? 'admin' : $this->getKey()),
482
            function () use ($addUsers) {
483
                return $this->getAllowedGroupsAndUsersProcess($addUsers);
484
            }
485
        );
486
487
        return $allowedGroupsAndUsers;
488
    }
489
490
    protected function getAllowedGroupUuidsProcess()
491
    {
492
        $allowedUserUuids = collect([$this->uuid]);
493
494
        if ($this->is_admin) {
495
            $groups = Group::all();
496
        } else {
497
            $groups = [];
498
            $users = [];
499
500
            foreach ($this->groups as $group) {
501
                $groups[$group->uuid] = $group;
502
            };
503
504
            $this->addRecursiveChildrenGroups($groups, $users, $groups, false);
505
506
            $groups = collect($groups);
507
        }
508
509
        foreach ($groups as $uuid => $group) {
510
            $allowedUserUuids[] = $uuid;
511
        }
512
513
        return $allowedUserUuids;
514
    }
515
516
    protected function addRecursiveChildrenGroups(&$groups, &$users, $searchGroups, $addUsers = false)
517
    {
518
        foreach ($searchGroups as $uuid => $searchGroup) {
519
            $searchChildrenGroups = [];
520
521
            foreach ($searchGroup->childrenGroups as $childrenGroup) {
522
                if (empty($groups[$childrenGroup->uuid])) {
523
                    $groups[$childrenGroup->uuid] = $childrenGroup;
524
                    $searchChildrenGroups[$childrenGroup->uuid] = $childrenGroup;
525
                }
526
527
                if($addUsers)
528
                {
529
                    foreach ($childrenGroup->users as $user) {
530
                        if (empty($users[$user->uuid])) {
531
                            $users[$user->uuid] = $user;
532
                        }
533
                    }
534
                }
535
            }
536
537
            $this->addRecursiveChildrenGroups($groups, $users, $searchChildrenGroups, $addUsers);
538
        }
539
    }
540
541
    protected function getAllowedGroupsAndUsersProcess($addUsers = true)
542
    {
543
        $allowedUserUuids = collect([[
544
            'uuid' => $this->uuid,
545
            'recordLabel' => uctrans('me', $this->module)
546
        ]]);
547
548
        if ($this->is_admin) {
549
            $groups = Group::orderBy('name')->get();
550
            $users  = \App\User::orderBy('name')->get();
0 ignored issues
show
Bug introduced by
The type App\User was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
551
        } else {
552
            $groups = [];
553
            $users = [];
554
555
            foreach ($this->groups as $group) {
556
                $groups[$group->uuid] = $group;
557
558
                if($addUsers)
559
                {
560
                    foreach ($group->users as $user) {
561
                        if (empty($users[$user->uuid])) {
562
                            $users[$user->uuid] = $user;
563
                        }
564
                    }
565
                }
566
            };
567
568
            $this->addRecursiveChildrenGroups($groups, $users, $groups, $addUsers);
569
570
            $groups = collect($groups)->sortBy('name');
571
            $users  = collect($users)->sortBy('name');
572
        }
573
574
        foreach ($groups as $uuid => $group) {
575
            $allowedUserUuids[] = [
576
                'uuid' => $group->uuid,
577
                'recordLabel' => $group->recordLabel
578
            ];
579
        }
580
581
        foreach ($users as $uuid => $user) {
582
            if($user->getKey() != $this->getKey()) {
583
                $allowedUserUuids[] = [
584
                    'uuid' => $user->uuid,
585
                    'recordLabel' => $user->recordLabel
586
                ];
587
            }
588
        }
589
590
        return $allowedUserUuids;
591
    }
592
}
593