Completed
Push — master ( 9e59ff...11ca4d )
by Abdelrahman
66:39 queued 62:06
created

User::getManagedAbilities()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cortex\Auth\Models;
6
7
use Error;
8
use Exception;
9
use BadMethodCallException;
10
use Rinvex\Country\Country;
11
use Rinvex\Language\Language;
12
use Rinvex\Tags\Traits\Taggable;
13
use Illuminate\Support\Collection;
14
use Illuminate\Auth\Authenticatable;
15
use Illuminate\Support\Facades\Hash;
16
use Rinvex\Auth\Traits\HasHashables;
17
use Rinvex\Auth\Traits\CanVerifyEmail;
18
use Rinvex\Auth\Traits\CanVerifyPhone;
19
use Cortex\Foundation\Traits\Auditable;
20
use Illuminate\Database\Eloquent\Model;
21
use Rinvex\Cacheable\CacheableEloquent;
22
use Rinvex\Support\Traits\HashidsTrait;
23
use Illuminate\Notifications\Notifiable;
24
use Illuminate\Support\Traits\Macroable;
25
use Rinvex\Auth\Traits\CanResetPassword;
26
use Rinvex\Support\Traits\ValidatingTrait;
27
use Spatie\MediaLibrary\HasMedia\HasMedia;
28
use Spatie\Activitylog\Traits\LogsActivity;
29
use Spatie\Activitylog\Traits\CausesActivity;
30
use Rinvex\Support\Traits\HasSocialAttributes;
31
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
32
use Rinvex\Auth\Traits\AuthenticatableTwoFactor;
33
use Rinvex\Auth\Contracts\CanVerifyEmailContract;
34
use Rinvex\Auth\Contracts\CanVerifyPhoneContract;
35
use Silber\Bouncer\Database\HasRolesAndAbilities;
36
use Illuminate\Foundation\Auth\Access\Authorizable;
37
use Rinvex\Auth\Contracts\CanResetPasswordContract;
38
use Illuminate\Database\Eloquent\Relations\MorphMany;
39
use Rinvex\Auth\Contracts\AuthenticatableTwoFactorContract;
40
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
41
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
42
43
abstract class User extends Model implements AuthenticatableContract, AuthenticatableTwoFactorContract, AuthorizableContract, CanResetPasswordContract, CanVerifyEmailContract, CanVerifyPhoneContract, HasMedia
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 208 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
44
{
45
    // @TODO: Strangely, this issue happens only here!!!
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
46
    // Duplicate trait usage to fire attached events for cache
47
    // flush before other events in other traits specially CausesActivity,
48
    // otherwise old cached queries used and no changelog recorded on update.
49
    use CacheableEloquent;
50
    use Taggable;
51
    use Auditable;
52
    use Macroable {
53
        Macroable::__call as macroableCall;
54
        Macroable::__callStatic as macroableCallStatic;
55
    }
56
    use Notifiable;
57
    use HashidsTrait;
58
    use Authorizable;
59
    use HasHashables;
60
    use LogsActivity;
61
    use HasMediaTrait;
62
    use CanVerifyEmail;
63
    use CausesActivity;
64
    use CanVerifyPhone;
65
    use Authenticatable;
66
    use ValidatingTrait;
67
    use CanResetPassword;
68
    use HasSocialAttributes;
69
    use HasRolesAndAbilities;
70
    use AuthenticatableTwoFactor;
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    protected $fillable = [
76
        'username',
77
        'password',
78
        'two_factor',
79
        'email',
80
        'email_verified_at',
81
        'phone',
82
        'phone_verified_at',
83
        'given_name',
84
        'family_name',
85
        'title',
86
        'organization',
87
        'country_code',
88
        'language_code',
89
        'birthday',
90
        'gender',
91
        'social',
92
        'is_active',
93
        'last_activity',
94
        'abilities',
95
        'roles',
96
        'tags',
97
    ];
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    protected $casts = [
103
        'username' => 'string',
104
        'password' => 'string',
105
        'two_factor' => 'array',
106
        'email' => 'string',
107
        'email_verified_at' => 'datetime',
108
        'phone' => 'string',
109
        'phone_verified_at' => 'datetime',
110
        'given_name' => 'string',
111
        'family_name' => 'string',
112
        'title' => 'string',
113
        'organization' => 'string',
114
        'country_code' => 'string',
115
        'language_code' => 'string',
116
        'birthday' => 'string',
117
        'gender' => 'string',
118
        'social' => 'array',
119
        'is_active' => 'boolean',
120
        'last_activity' => 'datetime',
121
        'deleted_at' => 'datetime',
122
    ];
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    protected $hidden = [
128
        'password',
129
        'two_factor',
130
        'remember_token',
131
    ];
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    protected $observables = [
137
        'validating',
138
        'validated',
139
    ];
140
141
    /**
142
     * The attributes to be encrypted before saving.
143
     *
144
     * @var array
145
     */
146
    protected $hashables = [
147
        'password',
148
    ];
149
150
    /**
151
     * The default rules that the model will validate against.
152
     *
153
     * @var array
154
     */
155
    protected $rules = [];
156
157
    /**
158
     * Whether the model should throw a
159
     * ValidationException if it fails validation.
160
     *
161
     * @var bool
162
     */
163
    protected $throwValidationExceptions = true;
164
165
    /**
166
     * Indicates whether to log only dirty attributes or all.
167
     *
168
     * @var bool
169
     */
170
    protected static $logOnlyDirty = true;
171
172
    /**
173
     * The attributes that are logged on change.
174
     *
175
     * @var array
176
     */
177
    protected static $logFillable = true;
178
179
    /**
180
     * The attributes that are ignored on change.
181
     *
182
     * @var array
183
     */
184
    protected static $ignoreChangedAttributes = [
185
        'password',
186
        'two_factor',
187
        'email_verified_at',
188
        'phone_verified_at',
189
        'last_activity',
190
        'created_at',
191
        'updated_at',
192
        'deleted_at',
193
    ];
194
195
    /**
196
     * Register media collections.
197
     *
198
     * @return void
199
     */
200
    public function registerMediaCollections(): void
201
    {
202
        $this->addMediaCollection('profile_picture')->singleFile();
203
        $this->addMediaCollection('cover_photo')->singleFile();
204
    }
205
206
    /**
207
     * Attach the given abilities to the model.
208
     *
209
     * @param mixed $abilities
210
     *
211
     * @return void
212
     */
213
    public function setAbilitiesAttribute($abilities): void
214
    {
215
        static::saved(function (self $model) use ($abilities) {
216
            $abilities = collect($abilities)->filter();
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $abilities, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
217
218
            $model->abilities->pluck('id')->similar($abilities)
219
            || activity()
220
                ->performedOn($model)
221
                ->withProperties(['attributes' => ['abilities' => $abilities], 'old' => ['abilities' => $model->abilities->pluck('id')->toArray()]])
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
222
                ->log('updated');
223
224
            $model->abilities()->sync($abilities, true);
225
        });
226
    }
227
228
    /**
229
     * Attach the given roles to the model.
230
     *
231
     * @param mixed $roles
232
     *
233
     * @return void
234
     */
235
    public function setRolesAttribute($roles): void
236
    {
237
        static::saved(function (self $model) use ($roles) {
238
            $roles = collect($roles)->filter();
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $roles, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
239
240
            $model->roles->pluck('id')->similar($roles)
241
            || activity()
242
                ->performedOn($model)
243
                ->withProperties(['attributes' => ['roles' => $roles], 'old' => ['roles' => $model->roles->pluck('id')->toArray()]])
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
244
                ->log('updated');
245
246
            $model->roles()->sync($roles, true);
247
        });
248
    }
249
250
    /**
251
     * {@inheritdoc}
252
     */
253
    protected static function boot()
254
    {
255
        parent::boot();
256
257
        static::saving(function (self $user) {
258
            foreach (array_intersect($user->getHashables(), array_keys($user->getAttributes())) as $hashable) {
259
                if ($user->isDirty($hashable) && Hash::needsRehash($user->{$hashable})) {
260
                    $user->{$hashable} = Hash::make($user->{$hashable});
261
                }
262
            }
263
        });
264
    }
265
266
    /**
267
     * The user may have many sessions.
268
     *
269
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
270
     */
271
    public function sessions(): MorphMany
272
    {
273
        return $this->morphMany(config('cortex.auth.models.session'), 'user');
274
    }
275
276
    /**
277
     * The user may have many socialites.
278
     *
279
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
280
     */
281
    public function socialites(): MorphMany
282
    {
283
        return $this->morphMany(config('cortex.auth.models.socialite'), 'user');
284
    }
285
286
    /**
287
     * Route notifications for the authy channel.
288
     *
289
     * @return int|null
290
     */
291
    public function routeNotificationForAuthy(): ?int
292
    {
293
        if (! ($authyId = array_get($this->getTwoFactor(), 'phone.authy_id')) && $this->getEmailForVerification() && $this->getPhoneForVerification() && $this->getCountryForVerification()) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 190 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
294
            $result = app('rinvex.authy.user')->register($this->getEmailForVerification(), preg_replace('/[^0-9]/', '', $this->getPhoneForVerification()), $this->getCountryForVerification());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 191 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
295
            $authyId = $result->get('user')['id'];
296
297
            // Prepare required variables
298
            $twoFactor = $this->getTwoFactor();
299
300
            // Update user account
301
            array_set($twoFactor, 'phone.authy_id', $authyId);
302
303
            $this->fill(['two_factor' => $twoFactor])->forceSave();
304
        }
305
306
        return $authyId;
307
    }
308
309
    /**
310
     * Get the user's country.
311
     *
312
     * @return \Rinvex\Country\Country
313
     */
314
    public function getCountryAttribute(): Country
315
    {
316
        return country($this->country_code);
317
    }
318
319
    /**
320
     * Get the user's language.
321
     *
322
     * @return \Rinvex\Language\Language
323
     */
324
    public function getLanguageAttribute(): Language
325
    {
326
        return language($this->language_code);
327
    }
328
329
    /**
330
     * Get full name attribute.
331
     *
332
     * @return string
333
     */
334
    public function getFullNameAttribute(): string
335
    {
336
        return implode(' ', [$this->given_name, $this->family_name]);
337
    }
338
339
    /**
340
     * Activate the user.
341
     *
342
     * @return $this
343
     */
344
    public function activate()
345
    {
346
        $this->update(['is_active' => true]);
347
348
        return $this;
349
    }
350
351
    /**
352
     * Deactivate the user.
353
     *
354
     * @return $this
355
     */
356
    public function deactivate()
357
    {
358
        $this->update(['is_active' => false]);
359
360
        return $this;
361
    }
362
363
    /**
364
     * Get managed roles.
365
     *
366
     * @return \Illuminate\Support\Collection
367
     */
368
    public function getManagedRoles(): Collection
369
    {
370
        if ($this->isA('superadmin')) {
371
            $roles = app('cortex.auth.role')->all();
372
        } elseif ($this->isA('supermanager')) {
373
            $roles = $this->roles->merge(config('rinvex.tenants.active') ? app('cortex.auth.role')->where('scope', config('rinvex.tenants.active')->getKey())->get() : collect());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 178 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
374
        } else {
375
            $roles = $this->roles;
376
        }
377
378
        return $roles->pluck('title', 'id')->sort();
379
    }
380
381
    /**
382
     * Get managed abilites.
383
     *
384
     * @return \Illuminate\Support\Collection
385
     */
386
    public function getManagedAbilities(): Collection
387
    {
388
        $abilities = $this->isA('superadmin') ? app('cortex.auth.ability')->all() : $this->getAbilities();
389
390
        return $abilities->groupBy('entity_type')->map->pluck('title', 'id')->sortKeys();
391
    }
392
393
    /**
394
     * Get the route key for the model.
395
     *
396
     * @return string
397
     */
398
    public function getRouteKeyName()
399
    {
400
        return 'username';
401
    }
402
403
    /**
404
     * Handle dynamic method calls into the model.
405
     *
406
     * @param string $method
407
     * @param array  $parameters
408
     *
409
     * @return mixed
410
     */
411
    public function __call($method, $parameters)
412
    {
413
        if (in_array($method, ['increment', 'decrement'])) {
414
            return $this->{$method}(...$parameters);
415
        }
416
417
        try {
418
            return $this->forwardCallTo($this->newQuery(), $method, $parameters);
419
        } catch (Error | BadMethodCallException $e) {
420
            if ($method !== 'macroableCall') {
421
                return $this->macroableCall($method, $parameters);
422
            }
423
        }
424
    }
425
426
    /**
427
     * Handle dynamic static method calls into the method.
428
     *
429
     * @param string $method
430
     * @param array  $parameters
431
     *
432
     * @return mixed
433
     */
434
    public static function __callStatic($method, $parameters)
435
    {
436
        try {
437
            return (new static())->{$method}(...$parameters);
438
        } catch (Exception $e) {
439
            if ($method !== 'macroableCallStatic') {
440
                return (new static())::macroableCallStatic($method, $parameters);
441
            }
442
        }
443
    }
444
}
445