mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Implement RBAC (only built in roles) (#15212)
* Install bouncer * Seeder and level migration * Display and edit roles * remove unused deluser page * Update Radius and SSO to assign roles * update AlertUtil direct level check to use roles instead * rewrite ircbot auth handling * Remove legacy auth getUserlist and getUserlevel methods, add getRoles Set roles in LegacyUserProvider * Small cleanups * centralize role sync code show roles on user preferences page * VueSelect component WIP and a little docs * WIP * SelectControllers id and text fields. * LibrenmsSelect component extracted from SettingSelectDynamic * Handle multiple selections * allow type coercion * full width settings * final style adjustments * Final compiled assets update * Style fixes * Fix SSO tests * Lint cleanups * small style fix * don't use json yet * Update baseline for usptream package issues * Change schema, not 100% sure it is correct not sure why xor doesn't work
This commit is contained in:
@@ -20,13 +20,16 @@
|
||||
|
||||
namespace LibreNMS;
|
||||
|
||||
use LibreNMS\Authentication\LegacyAuth;
|
||||
use App\Models\Device;
|
||||
use App\Models\Eventlog;
|
||||
use App\Models\Port;
|
||||
use App\Models\Service;
|
||||
use App\Models\User;
|
||||
use LibreNMS\DB\Eloquent;
|
||||
use LibreNMS\Enum\AlertState;
|
||||
use LibreNMS\Util\Number;
|
||||
use LibreNMS\Util\Time;
|
||||
use LibreNMS\Util\Version;
|
||||
use Permissions;
|
||||
|
||||
class IRCBot
|
||||
{
|
||||
@@ -657,18 +660,11 @@ class IRCBot
|
||||
$this->log("HostAuth on irc matching $host to " . $this->getUserHost($this->data));
|
||||
}
|
||||
if (preg_match("/$host/", $this->getUserHost($this->data))) {
|
||||
$user_id = LegacyAuth::get()->getUserid($nms_user);
|
||||
$user = LegacyAuth::get()->getUser($user_id);
|
||||
$this->user['name'] = $user['username'];
|
||||
$this->user['id'] = $user_id;
|
||||
$this->user['level'] = LegacyAuth::get()->getUserlevel($user['username']);
|
||||
$user = User::firstWhere('username', $nms_user);
|
||||
$this->user['user'] = $user;
|
||||
$this->user['expire'] = (time() + ($this->config['irc_authtime'] * 3600));
|
||||
if ($this->user['level'] < 5) {
|
||||
$this->user['devices'] = Permissions::devicesForUser($this->user['id'])->toArray();
|
||||
$this->user['ports'] = Permissions::portsForUser($this->user['id'])->toArray();
|
||||
}
|
||||
if ($this->debug) {
|
||||
$this->log("HostAuth on irc for '" . $user['username'] . "', ID: '" . $user_id . "', Host: '" . $host);
|
||||
$this->log("HostAuth on irc for '" . $user->username . "', ID: '" . $user->user_id . "', Host: '" . $host);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -695,31 +691,22 @@ class IRCBot
|
||||
if (strlen($params[0]) == 64) {
|
||||
if ($this->tokens[$this->getUser($this->data)] == $params[0]) {
|
||||
$this->user['expire'] = (time() + ($this->config['irc_authtime'] * 3600));
|
||||
$tmp_user = LegacyAuth::get()->getUser($this->user['id']);
|
||||
$tmp = LegacyAuth::get()->getUserlevel($tmp_user['username']);
|
||||
$this->user['level'] = $tmp;
|
||||
if ($this->user['level'] < 5) {
|
||||
$this->user['devices'] = Permissions::devicesForUser($this->user['id'])->toArray();
|
||||
$this->user['ports'] = Permissions::portsForUser($this->user['id'])->toArray();
|
||||
}
|
||||
|
||||
return $this->respond('Authenticated.');
|
||||
} else {
|
||||
return $this->respond('Nope.');
|
||||
}
|
||||
} else {
|
||||
$user_id = LegacyAuth::get()->getUserid($params[0]);
|
||||
$user = LegacyAuth::get()->getUser($user_id);
|
||||
if ($user['email'] && $user['username'] == $params[0]) {
|
||||
$user = User::firstWhere('username', $params[0]);
|
||||
if ($user->email && $user->username == $params[0]) {
|
||||
$token = hash('gost', openssl_random_pseudo_bytes(1024));
|
||||
$this->tokens[$this->getUser($this->data)] = $token;
|
||||
$this->user['name'] = $params[0];
|
||||
$this->user['id'] = $user['user_id'];
|
||||
$this->user['user'] = $user;
|
||||
if ($this->debug) {
|
||||
$this->log("Auth for '" . $params[0] . "', ID: '" . $user['user_id'] . "', Token: '" . $token . "', Mail: '" . $user['email'] . "'");
|
||||
$this->log("Auth for '" . $params[0] . "', ID: '" . $user->user_id . "', Token: '" . $token . "', Mail: '" . $user->email . "'");
|
||||
}
|
||||
|
||||
if (send_mail($user['email'], 'LibreNMS IRC-Bot Authtoken', "Your Authtoken for the IRC-Bot:\r\n\r\n" . $token . "\r\n\r\n") === true) {
|
||||
if (send_mail($user->email, 'LibreNMS IRC-Bot Authtoken', "Your Authtoken for the IRC-Bot:\r\n\r\n" . $token . "\r\n\r\n") === true) {
|
||||
return $this->respond('Token sent!');
|
||||
} else {
|
||||
return $this->respond('Sorry, seems like mail doesnt like us.');
|
||||
@@ -734,7 +721,7 @@ class IRCBot
|
||||
|
||||
private function _reload($params)
|
||||
{
|
||||
if ($this->user['level'] == 10) {
|
||||
if ($this->user['user']->can('irc.reload')) {
|
||||
if ($params == 'external') {
|
||||
$this->respond('Reloading external scripts.');
|
||||
|
||||
@@ -756,7 +743,7 @@ class IRCBot
|
||||
|
||||
private function _join($params)
|
||||
{
|
||||
if ($this->user['level'] == 10) {
|
||||
if ($this->user['user']->can('irc.join')) {
|
||||
return $this->joinChan($params);
|
||||
} else {
|
||||
return $this->respond('Permission denied.');
|
||||
@@ -767,7 +754,7 @@ class IRCBot
|
||||
|
||||
private function _quit($params)
|
||||
{
|
||||
if ($this->user['level'] == 10) {
|
||||
if ($this->user['user']->can('irc.quit')) {
|
||||
$this->ircRaw('QUIT :Requested');
|
||||
|
||||
return exit;
|
||||
@@ -812,31 +799,30 @@ class IRCBot
|
||||
if (strlen($params[1]) > 0) {
|
||||
$hostname = preg_replace("/[^A-z0-9\.\-]/", '', $params[1]);
|
||||
}
|
||||
$hostname = $hostname . '%';
|
||||
if ($this->user['level'] < 5) {
|
||||
$tmp = dbFetchRows('SELECT `event_id`, eventlog.device_id, devices.hostname, `datetime`,`message`, eventlog.type FROM `eventlog`, `devices` WHERE eventlog.device_id=devices.device_id and devices.hostname like "' . $hostname . '" and eventlog.device_id IN (' . implode(',', $this->user['devices']) . ') ORDER BY `event_id` DESC LIMIT ' . (int) $num);
|
||||
} else {
|
||||
$tmp = dbFetchRows('SELECT `event_id`, eventlog.device_id, devices.hostname, `datetime`,`message`, eventlog.type FROM `eventlog`, `devices` WHERE eventlog.device_id=devices.device_id and devices.hostname like "' . $hostname . '" ORDER BY `event_id` DESC LIMIT ' . (int) $num);
|
||||
}
|
||||
|
||||
$tmp = Eventlog::with('device')->hasAccess($this->user['user'])->whereIn('device_id', function ($query) use ($hostname) {
|
||||
return $query->where('hostname', 'like', $hostname . '%')->select('device_id');
|
||||
})->select(['event_id', 'datetime', 'type', 'message'])->orderBy('event_id')->limit((int) $num)->get();
|
||||
|
||||
/** @var Eventlog $logline */
|
||||
foreach ($tmp as $logline) {
|
||||
$response = $logline['datetime'] . ' ';
|
||||
$response .= $this->_color($logline['hostname'], null, null, 'bold') . ' ';
|
||||
$response = $logline->datetime . ' ';
|
||||
$response .= $this->_color($logline->device->displayName(), null, null, 'bold') . ' ';
|
||||
if ($this->config['irc_alert_utf8']) {
|
||||
if (preg_match('/critical alert/', $logline['message'])) {
|
||||
$response .= preg_replace('/critical alert/', $this->_color('critical alert', 'red'), $logline['message']) . ' ';
|
||||
} elseif (preg_match('/warning alert/', $logline['message'])) {
|
||||
$response .= preg_replace('/warning alert/', $this->_color('warning alert', 'yellow'), $logline['message']) . ' ';
|
||||
} elseif (preg_match('/recovery/', $logline['message'])) {
|
||||
$response .= preg_replace('/recovery/', $this->_color('recovery', 'green'), $logline['message']) . ' ';
|
||||
if (preg_match('/critical alert/', $logline->message)) {
|
||||
$response .= preg_replace('/critical alert/', $this->_color('critical alert', 'red'), $logline->message) . ' ';
|
||||
} elseif (preg_match('/warning alert/', $logline->message)) {
|
||||
$response .= preg_replace('/warning alert/', $this->_color('warning alert', 'yellow'), $logline->message) . ' ';
|
||||
} elseif (preg_match('/recovery/', $logline->message)) {
|
||||
$response .= preg_replace('/recovery/', $this->_color('recovery', 'green'), $logline->message) . ' ';
|
||||
} else {
|
||||
$response .= $logline['message'] . ' ';
|
||||
$response .= $logline->message . ' ';
|
||||
}
|
||||
} else {
|
||||
$response .= $logline['message'] . ' ';
|
||||
$response .= $logline->message . ' ';
|
||||
}
|
||||
if ($logline['type'] != 'NULL') {
|
||||
$response .= $logline['type'] . ' ';
|
||||
if ($logline->type != 'NULL') {
|
||||
$response .= $logline->type . ' ';
|
||||
}
|
||||
if ($this->config['irc_floodlimit'] > 100) {
|
||||
$this->floodcount += strlen($response);
|
||||
@@ -862,23 +848,12 @@ class IRCBot
|
||||
|
||||
private function _down($params)
|
||||
{
|
||||
if ($this->user['level'] < 5) {
|
||||
$tmp = dbFetchRows('SELECT `hostname` FROM `devices` WHERE status=0 AND `device_id` IN (' . implode(',', $this->user['devices']) . ')');
|
||||
} else {
|
||||
$tmp = dbFetchRows('SELECT `hostname` FROM `devices` WHERE status=0');
|
||||
}
|
||||
$devices = Device::hasAccess($this->user['user'])->isDown()
|
||||
->select(['device_id', 'hostname', 'sysName', 'display', 'ip'])->get();
|
||||
|
||||
$msg = '';
|
||||
foreach ($tmp as $db) {
|
||||
if ($db['hostname']) {
|
||||
$msg .= ', ' . $db['hostname'];
|
||||
}
|
||||
}
|
||||
$msg = $devices->map->displayName()->implode(', ');
|
||||
|
||||
$msg = substr($msg, 2);
|
||||
$msg = $msg ? $msg : 'Nothing to show :)';
|
||||
|
||||
return $this->respond($msg);
|
||||
return $this->respond($msg ?: 'Nothing to show :)');
|
||||
}
|
||||
|
||||
//end _down()
|
||||
@@ -887,20 +862,16 @@ class IRCBot
|
||||
{
|
||||
$params = explode(' ', $params);
|
||||
$hostname = $params[0];
|
||||
$device = dbFetchRow('SELECT * FROM `devices` WHERE `hostname` = ?', [$hostname]);
|
||||
$device = Device::hasAccess($this->user['user'])->firstWhere('hostname', $hostname);
|
||||
if (! $device) {
|
||||
return $this->respond('Error: Bad or Missing hostname, use .listdevices to show all devices.');
|
||||
}
|
||||
|
||||
if ($this->user['level'] < 5 && ! in_array($device['device_id'], $this->user['devices'])) {
|
||||
return $this->respond('Error: Permission denied.');
|
||||
}
|
||||
$status = $device->status ? 'Up ' . Time::formatInterval($device->uptime) : 'Down';
|
||||
$status .= $device->ignore ? '*Ignored*' : '';
|
||||
$status .= $device->disabled ? '*Disabled*' : '';
|
||||
|
||||
$status = $device['status'] ? 'Up ' . Time::formatInterval($device['uptime']) : 'Down';
|
||||
$status .= $device['ignore'] ? '*Ignored*' : '';
|
||||
$status .= $device['disabled'] ? '*Disabled*' : '';
|
||||
|
||||
return $this->respond($device['os'] . ' ' . $device['version'] . ' ' . $device['features'] . ' ' . $status);
|
||||
return $this->respond($device->displayName() . ': ' . $device->os . ' ' . $device->version . ' ' . $device->features . ' ' . $status);
|
||||
}
|
||||
|
||||
//end _device()
|
||||
@@ -914,10 +885,14 @@ class IRCBot
|
||||
return $this->respond('Error: Missing hostname or ifname.');
|
||||
}
|
||||
|
||||
$device = dbFetchRow('SELECT * FROM `devices` WHERE `hostname` = ?', [$hostname]);
|
||||
$port = dbFetchRow('SELECT * FROM `ports` WHERE (`ifName` = ? OR `ifDescr` = ?) AND device_id = ?', [$ifname, $ifname, $device['device_id']]);
|
||||
if ($this->user['level'] < 5 && ! in_array($port['port_id'], $this->user['ports']) && ! in_array($device['device_id'], $this->user['devices'])) {
|
||||
return $this->respond('Error: Permission denied.');
|
||||
$device = Device::hasAccess($this->user['user'])->firstWhere('hostname', $hostname);
|
||||
if (! $device) {
|
||||
return $this->respond('Error: Bad or Missing hostname, use .listdevices to show all devices.');
|
||||
}
|
||||
|
||||
$port = $device->ports()->hasAccess($this->user['user'])->where('ifName', $ifname)->orWhere('ifDescr', $ifname);
|
||||
if (! $port) {
|
||||
return $this->respond('Error: Port not found or you do not have access.');
|
||||
}
|
||||
|
||||
$bps_in = Number::formatSi($port['ifInOctets_rate'] * 8, 2, 3, 'bps');
|
||||
@@ -932,21 +907,11 @@ class IRCBot
|
||||
|
||||
private function _listdevices($params)
|
||||
{
|
||||
if ($this->user['level'] < 5) {
|
||||
$tmp = dbFetchRows('SELECT `hostname` FROM `devices` WHERE `device_id` IN (' . implode(',', $this->user['devices']) . ')');
|
||||
} else {
|
||||
$tmp = dbFetchRows('SELECT `hostname` FROM `devices`');
|
||||
}
|
||||
$devices = Device::hasAccess($this->user['user'])->pluck('hostname');
|
||||
|
||||
$msg = '';
|
||||
foreach ($tmp as $device) {
|
||||
$msg .= ', ' . $device['hostname'];
|
||||
}
|
||||
$msg = $devices->implode(', ');
|
||||
|
||||
$msg = substr($msg, 2);
|
||||
$msg = $msg ? $msg : 'Nothing to show..?';
|
||||
|
||||
return $this->respond($msg);
|
||||
return $this->respond($msg ?: 'Nothing to show..?');
|
||||
}
|
||||
|
||||
//end _listdevices()
|
||||
@@ -956,26 +921,15 @@ class IRCBot
|
||||
$params = explode(' ', $params);
|
||||
$statustype = $params[0];
|
||||
|
||||
$d_w = '';
|
||||
$d_a = '';
|
||||
$p_w = '';
|
||||
$p_a = '';
|
||||
if ($this->user['level'] < 5) {
|
||||
$d_w = ' WHERE device_id IN (' . implode(',', $this->user['devices']) . ')';
|
||||
$d_a = ' AND device_id IN (' . implode(',', $this->user['devices']) . ')';
|
||||
$p_w = ' WHERE port_id IN (' . implode(',', $this->user['ports']) . ') OR device_id IN (' . implode(',', $this->user['devices']) . ')';
|
||||
$p_a = ' AND (I.port_id IN (' . implode(',', $this->user['ports']) . ') OR I.device_id IN (' . implode(',', $this->user['devices']) . '))';
|
||||
}
|
||||
|
||||
switch ($statustype) {
|
||||
case 'devices':
|
||||
case 'device':
|
||||
case 'dev':
|
||||
$devcount = dbFetchCell('SELECT count(*) FROM devices' . $d_w);
|
||||
$devup = dbFetchCell("SELECT count(*) FROM devices WHERE status = '1' AND `ignore` = '0'" . $d_a);
|
||||
$devdown = dbFetchCell("SELECT count(*) FROM devices WHERE status = '0' AND `ignore` = '0'" . $d_a);
|
||||
$devign = dbFetchCell("SELECT count(*) FROM devices WHERE `ignore` = '1'" . $d_a);
|
||||
$devdis = dbFetchCell("SELECT count(*) FROM devices WHERE `disabled` = '1'" . $d_a);
|
||||
$devcount = Device::hasAccess($this->user['user'])->count();
|
||||
$devup = Device::hasAccess($this->user['user'])->isUp()->count();
|
||||
$devdown = Device::hasAccess($this->user['user'])->isDown()->count();
|
||||
$devign = Device::hasAccess($this->user['user'])->isIgnored()->count();
|
||||
$devdis = Device::hasAccess($this->user['user'])->isDisabled()->count();
|
||||
if ($devup > 0) {
|
||||
$devup = $this->_color($devup, 'green');
|
||||
}
|
||||
@@ -991,11 +945,13 @@ class IRCBot
|
||||
case 'ports':
|
||||
case 'port':
|
||||
case 'prt':
|
||||
$prtcount = dbFetchCell('SELECT count(*) FROM ports' . $p_w);
|
||||
$prtup = dbFetchCell("SELECT count(*) FROM ports AS I, devices AS D WHERE I.ifOperStatus = 'up' AND I.ignore = '0' AND I.device_id = D.device_id AND D.ignore = '0'" . $p_a);
|
||||
$prtdown = dbFetchCell("SELECT count(*) FROM ports AS I, devices AS D WHERE I.ifOperStatus = 'down' AND I.ifAdminStatus = 'up' AND I.ignore = '0' AND D.device_id = I.device_id AND D.ignore = '0'" . $p_a);
|
||||
$prtsht = dbFetchCell("SELECT count(*) FROM ports AS I, devices AS D WHERE I.ifAdminStatus = 'down' AND I.ignore = '0' AND D.device_id = I.device_id AND D.ignore = '0'" . $p_a);
|
||||
$prtign = dbFetchCell("SELECT count(*) FROM ports AS I, devices AS D WHERE D.device_id = I.device_id AND (I.ignore = '1' OR D.ignore = '1')" . $p_a);
|
||||
$prtcount = Port::hasAccess($this->user['user'])->count();
|
||||
$prtup = Port::hasAccess($this->user['user'])->isUp()->count();
|
||||
$prtdown = Port::hasAccess($this->user['user'])->isDown()->whereHas('device', fn ($q) => $q->where('ignore', 0))->count();
|
||||
$prtsht = Port::hasAccess($this->user['user'])->isShutdown()->whereHas('device', fn ($q) => $q->where('ignore', 0))->count();
|
||||
$prtign = Port::hasAccess($this->user['user'])->where(function ($query) {
|
||||
$query->isIgnored()->orWhereHas('device', fn ($q) => $q->where('ignore', 1));
|
||||
})->count();
|
||||
// $prterr = dbFetchCell("SELECT count(*) FROM ports AS I, devices AS D WHERE D.device_id = I.device_id AND (I.ignore = '0' OR D.ignore = '0') AND (I.ifInErrors_delta > '0' OR I.ifOutErrors_delta > '0')".$p_a);
|
||||
if ($prtup > 0) {
|
||||
$prtup = $this->_color($prtup, 'green');
|
||||
@@ -1014,15 +970,16 @@ class IRCBot
|
||||
case 'srv':
|
||||
$status_counts = [];
|
||||
$status_colors = [0 => 'green', 3 => 'lightblue', 1 => 'yellow', 2 => 'red'];
|
||||
$srvcount = dbFetchCell('SELECT COUNT(*) FROM services' . $d_w);
|
||||
$srvign = dbFetchCell('SELECT COUNT(*) FROM services WHERE service_ignore = 1' . $d_a);
|
||||
$srvdis = dbFetchCell('SELECT COUNT(*) FROM services WHERE service_disabled = 1' . $d_a);
|
||||
$service_status = dbFetchRows("SELECT `service_status`, COUNT(*) AS `count` FROM `services` WHERE `service_disabled`=0 AND `service_ignore`=0 $d_a GROUP BY `service_status`");
|
||||
$service_status = array_column($service_status, 'count', 'service_status'); // key by status
|
||||
$srvcount = Service::hasAccess($this->user['user'])->count();
|
||||
$srvign = Service::hasAccess($this->user['user'])->isIgnored()->count();
|
||||
$srvdis = Service::hasAccess($this->user['user'])->isDisabled()->count();
|
||||
$service_status = Service::hasAccess($this->user['user'])->isActive()->groupBy('service_status')
|
||||
->select('service_status', \DB::raw('count(*) as count'))->get()
|
||||
->pluck('count', 'service_status');
|
||||
|
||||
foreach ($status_colors as $status => $color) {
|
||||
if (isset($service_status[$status])) {
|
||||
$status_counts[$status] = $this->_color($service_status[$status], $color);
|
||||
if ($service_status->has($status)) {
|
||||
$status_counts[$status] = $this->_color($service_status->get($status), $color);
|
||||
$srvcount = $this->_color($srvcount, $color, null, 'bold'); // upgrade the main count color
|
||||
} else {
|
||||
$status_counts[$status] = 0;
|
||||
|
Reference in New Issue
Block a user