Files
librenms-librenms/app/Models/User.php
Tony Murray 5076deccf3 Improve the efficiency of some queries (#13974)
* Improve the efficiency of some queries
Mostly by switching from whereIn to whereIntegerInRaw.
This inserts integers directly into the query instead of using placeholders (also escapes them)

also remove extra json_encode/json_decode in PingCheck

* Fix return types

Probably will result in some missing baseline exceptions.

* Update PingCheck.php

* whitespace
2022-05-16 09:57:58 +02:00

255 lines
6.5 KiB
PHP

<?php
namespace App\Models;
use App\Events\UserCreated;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use LibreNMS\Authentication\LegacyAuth;
use NotificationChannels\WebPush\HasPushSubscriptions;
use Permissions;
/**
* @method static \Database\Factories\UserFactory factory(...$parameters)
*/
class User extends Authenticatable
{
use Notifiable, HasFactory, HasPushSubscriptions;
protected $primaryKey = 'user_id';
protected $fillable = ['realname', 'username', 'email', 'level', 'descr', 'can_modify_passwd', 'auth_type', 'auth_id', 'enabled'];
protected $hidden = ['password', 'remember_token', 'pivot'];
protected $attributes = [ // default values
'descr' => '',
'realname' => '',
'email' => '',
];
protected $dispatchesEvents = [
'created' => UserCreated::class,
];
protected $casts = [
'realname' => 'string',
'descr' => 'string',
'email' => 'string',
'can_modify_passwd' => 'integer',
];
// ---- Helper Functions ----
/**
* Test if this user has global read access
* these users have a level of 5, 10 or 11 (demo).
*
* @return bool
*/
public function hasGlobalRead()
{
return $this->hasGlobalAdmin() || $this->level == 5;
}
/**
* Test if this user has global admin access
* these users have a level of 10 or 11 (demo).
*
* @return bool
*/
public function hasGlobalAdmin()
{
return $this->level >= 10;
}
/**
* Test if the User is an admin.
*
* @return bool
*/
public function isAdmin()
{
return $this->level == 10;
}
/**
* Test if this user is the demo user
*
* @return bool
*/
public function isDemo()
{
return $this->level == 11;
}
/**
* Check if this user has access to a device
*
* @param Device|int $device can be a device Model or device id
* @return bool
*/
public function canAccessDevice($device)
{
return $this->hasGlobalRead() || Permissions::canAccessDevice($device, $this->user_id);
}
/**
* Helper function to hash passwords before setting
*
* @param string $password
*/
public function setPassword($password)
{
$this->attributes['password'] = $password ? Hash::make($password) : null;
}
/**
* Check if the given user can set the password for this user
*
* @param User $user
* @return bool
*/
public function canSetPassword($user)
{
if ($user && LegacyAuth::get()->canUpdatePasswords()) {
if ($user->isAdmin()) {
return true;
}
return $user->is($this) && $this->can_modify_passwd;
}
return false;
}
/**
* Checks if this user has a browser push notification transport configured.
*
* @return bool
*/
public function hasBrowserPushTransport(): bool
{
$user_id = \Auth::id();
return AlertTransport::query()
->where('transport_type', 'browserpush')
->where('transport_config', 'regexp', "\"user\":\"(0|$user_id)\"")
->exists();
}
// ---- Query scopes ----
/**
* This restricts the query to only users that match the current auth method
* It is not needed when using user_id, but should be used for username and auth_id
*
* @param Builder $query
* @return Builder
*/
public function scopeThisAuth($query)
{
// find user including ones where we might not know the auth type
$type = LegacyAuth::getType();
return $query->where(function ($query) use ($type) {
$query->where('auth_type', $type)
->orWhereNull('auth_type')
->orWhere('auth_type', '');
});
}
public function scopeAdminOnly($query)
{
$query->where('level', 10);
}
// ---- Accessors/Mutators ----
public function setRealnameAttribute($realname)
{
$this->attributes['realname'] = (string) $realname;
}
public function setDescrAttribute($descr)
{
$this->attributes['descr'] = (string) $descr;
}
public function setEmailAttribute($email)
{
$this->attributes['email'] = (string) $email;
}
public function setCanModifyPasswdAttribute($modify)
{
$this->attributes['can_modify_passwd'] = $modify ? 1 : 0;
}
public function setEnabledAttribute($enable)
{
$this->attributes['enabled'] = $enable ? 1 : 0;
}
public function getDevicesAttribute()
{
// pseudo relation
if (! array_key_exists('devices', $this->relations)) {
$this->setRelation('devices', $this->devices()->get());
}
return $this->getRelation('devices');
}
// ---- Define Relationships ----
public function apiTokens(): HasMany
{
return $this->hasMany(\App\Models\ApiToken::class, 'user_id', 'user_id');
}
public function devices()
{
// pseudo relation
return Device::query()->when(! $this->hasGlobalRead(), function ($query) {
return $query->whereIntegerInRaw('device_id', Permissions::devicesForUser($this));
});
}
public function deviceGroups(): BelongsToMany
{
return $this->belongsToMany(\App\Models\DeviceGroup::class, 'devices_group_perms', 'user_id', 'device_group_id');
}
public function ports()
{
if ($this->hasGlobalRead()) {
return Port::query();
} else {
//FIXME we should return all ports for a device if the user has been given access to the whole device.
return $this->belongsToMany(\App\Models\Port::class, 'ports_perms', 'user_id', 'port_id');
}
}
public function dashboards(): HasMany
{
return $this->hasMany(\App\Models\Dashboard::class, 'user_id');
}
public function notificationAttribs(): HasMany
{
return $this->hasMany(NotificationAttrib::class, 'user_id');
}
public function preferences(): HasMany
{
return $this->hasMany(\App\Models\UserPref::class, 'user_id');
}
public function widgets(): HasMany
{
return $this->hasMany(\App\Models\UserWidget::class, 'user_id');
}
}