1<?php
2
3namespace App\Models;
4
5use App\Events\UserCreated;
6use Illuminate\Database\Eloquent\Builder;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9use Illuminate\Database\Eloquent\Relations\HasMany;
10use Illuminate\Database\Eloquent\Relations\HasOne;
11use Illuminate\Foundation\Auth\User as Authenticatable;
12use Illuminate\Notifications\Notifiable;
13use Illuminate\Support\Facades\Hash;
14use LibreNMS\Authentication\LegacyAuth;
15use Permissions;
16
17/**
18 * @method static \Database\Factories\UserFactory factory(...$parameters)
19 */
20class User extends Authenticatable
21{
22    use Notifiable, HasFactory;
23
24    protected $primaryKey = 'user_id';
25    protected $fillable = ['realname', 'username', 'email', 'level', 'descr', 'can_modify_passwd', 'auth_type', 'auth_id', 'enabled'];
26    protected $hidden = ['password', 'remember_token', 'pivot'];
27    protected $attributes = [ // default values
28        'descr' => '',
29        'realname' => '',
30        'email' => '',
31    ];
32    protected $dispatchesEvents = [
33        'created' => UserCreated::class,
34    ];
35
36    protected $casts = [
37        'realname' => 'string',
38        'descr' => 'string',
39        'email' => 'string',
40        'can_modify_passwd' => 'integer',
41    ];
42
43    // ---- Helper Functions ----
44
45    /**
46     * Test if this user has global read access
47     * these users have a level of 5, 10 or 11 (demo).
48     *
49     * @return bool
50     */
51    public function hasGlobalRead()
52    {
53        return $this->hasGlobalAdmin() || $this->level == 5;
54    }
55
56    /**
57     * Test if this user has global admin access
58     * these users have a level of 10 or 11 (demo).
59     *
60     * @return bool
61     */
62    public function hasGlobalAdmin()
63    {
64        return $this->level >= 10;
65    }
66
67    /**
68     * Test if the User is an admin.
69     *
70     * @return bool
71     */
72    public function isAdmin()
73    {
74        return $this->level == 10;
75    }
76
77    /**
78     * Test if this user is the demo user
79     *
80     * @return bool
81     */
82    public function isDemo()
83    {
84        return $this->level == 11;
85    }
86
87    /**
88     * Check if this user has access to a device
89     *
90     * @param Device|int $device can be a device Model or device id
91     * @return bool
92     */
93    public function canAccessDevice($device)
94    {
95        return $this->hasGlobalRead() || Permissions::canAccessDevice($device, $this->user_id);
96    }
97
98    /**
99     * Helper function to hash passwords before setting
100     *
101     * @param string $password
102     */
103    public function setPassword($password)
104    {
105        $this->attributes['password'] = $password ? Hash::make($password) : null;
106    }
107
108    /**
109     * Check if the given user can set the password for this user
110     *
111     * @param User $user
112     * @return bool
113     */
114    public function canSetPassword($user)
115    {
116        if ($user && LegacyAuth::get()->canUpdatePasswords()) {
117            if ($user->isAdmin()) {
118                return true;
119            }
120
121            return $user->is($this) && $this->can_modify_passwd;
122        }
123
124        return false;
125    }
126
127    // ---- Query scopes ----
128
129    /**
130     * This restricts the query to only users that match the current auth method
131     * It is not needed when using user_id, but should be used for username and auth_id
132     *
133     * @param Builder $query
134     * @return Builder
135     */
136    public function scopeThisAuth($query)
137    {
138        // find user including ones where we might not know the auth type
139        $type = LegacyAuth::getType();
140
141        return $query->where(function ($query) use ($type) {
142            $query->where('auth_type', $type)
143                ->orWhereNull('auth_type')
144                ->orWhere('auth_type', '');
145        });
146    }
147
148    public function scopeAdminOnly($query)
149    {
150        $query->where('level', 10);
151    }
152
153    // ---- Accessors/Mutators ----
154
155    public function setRealnameAttribute($realname)
156    {
157        $this->attributes['realname'] = (string) $realname;
158    }
159
160    public function setDescrAttribute($descr)
161    {
162        $this->attributes['descr'] = (string) $descr;
163    }
164
165    public function setEmailAttribute($email)
166    {
167        $this->attributes['email'] = (string) $email;
168    }
169
170    public function setCanModifyPasswdAttribute($modify)
171    {
172        $this->attributes['can_modify_passwd'] = $modify ? 1 : 0;
173    }
174
175    public function setEnabledAttribute($enable)
176    {
177        $this->attributes['enabled'] = $enable ? 1 : 0;
178    }
179
180    public function getDevicesAttribute()
181    {
182        // pseudo relation
183        if (! array_key_exists('devices', $this->relations)) {
184            $this->setRelation('devices', $this->devices()->get());
185        }
186
187        return $this->getRelation('devices');
188    }
189
190    // ---- Define Relationships ----
191
192    public function apiToken(): HasOne
193    {
194        return $this->hasOne(\App\Models\ApiToken::class, 'user_id', 'user_id');
195    }
196
197    public function devices()
198    {
199        // pseudo relation
200        return Device::query()->when(! $this->hasGlobalRead(), function ($query) {
201            return $query->whereIn('device_id', Permissions::devicesForUser($this));
202        });
203    }
204
205    public function deviceGroups(): BelongsToMany
206    {
207        return $this->belongsToMany(\App\Models\DeviceGroup::class, 'devices_group_perms', 'user_id', 'device_group_id');
208    }
209
210    public function ports()
211    {
212        if ($this->hasGlobalRead()) {
213            return Port::query();
214        } else {
215            //FIXME we should return all ports for a device if the user has been given access to the whole device.
216            return $this->belongsToMany(\App\Models\Port::class, 'ports_perms', 'user_id', 'port_id');
217        }
218    }
219
220    public function dashboards(): HasMany
221    {
222        return $this->hasMany(\App\Models\Dashboard::class, 'user_id');
223    }
224
225    public function preferences(): HasMany
226    {
227        return $this->hasMany(\App\Models\UserPref::class, 'user_id');
228    }
229
230    public function widgets(): HasMany
231    {
232        return $this->hasMany(\App\Models\UserWidget::class, 'user_id');
233    }
234}
235