Completed
Push — develop ( c27d3d...2d098e )
by Abdelrahman
02:08
created

User::sessions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
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
Bug introduced by
It seems like $this->getTwoFactor() targeting Rinvex\Auth\Traits\Authe...oFactor::getTwoFactor() can also be of type null; however, array_get() does only seem to accept object<ArrayAccess>|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $twoFactor defined by $this->getTwoFactor() on line 298 can also be of type null; however, array_set() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
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
        } else if ($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