From c27d9c7387eb6d495ec3c2e3aa0320a28670543e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 18 Mar 2021 07:01:42 +0100 Subject: [PATCH] Monitoring, various: use monitoring permissions fixes #2304 --- application/controllers/HostController.php | 57 ++++++++++-- application/controllers/ServiceController.php | 5 ++ configuration.php | 16 ++++ doc/82-Changelog.md | 3 + library/Director/Monitoring.php | 90 +++++++++++++++++-- .../ProvidedHook/Monitoring/HostActions.php | 14 +++ .../Monitoring/ServiceActions.php | 17 ++-- library/Director/Web/Tabs/ObjectTabs.php | 2 +- 8 files changed, 184 insertions(+), 20 deletions(-) diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index 933b991f6..a6971d236 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Director\Controllers; use gipfl\Web\Widget\Hint; +use Icinga\Module\Director\Monitoring; use ipl\Html\Html; use gipfl\IcingaWeb2\Link; use gipfl\IcingaWeb2\Url; @@ -30,15 +31,59 @@ class HostController extends ObjectController { protected function checkDirectorPermissions() { - if (in_array($this->getRequest()->getActionName(), [ + if ($this->isServiceAction() && (new Monitoring())->authCanEditService( + $this->Auth(), + $this->getParam('name'), + $this->getParam('service') + )) { + return; + } + + if ($this->isServicesReadOnlyAction()) { + $this->assertPermission('director/monitoring/services-ro'); + return; + } + + if ($this->hasPermission('director/hosts')) { // faster + return; + } + + if ($this->canModifyHostViaMonitoringPermissions($this->getParam('name'))) { + return; + } + + $this->assertPermission('director/hosts'); // complain about default hosts permission + } + + protected function isServicesReadOnlyAction() + { + return in_array($this->getRequest()->getActionName(), [ 'servicesro', 'findservice', - 'invalidservice' - ])) { - $this->assertPermission('director/monitoring/services-ro'); - } else { - $this->assertPermission('director/hosts'); + 'invalidservice', + ]); + } + + protected function isServiceAction() + { + return in_array($this->getRequest()->getActionName(), [ + 'servicesro', + 'findservice', + 'invalidservice', + 'servicesetservice', + 'appliedservice', + 'inheritedservice', + ]); + } + + protected function canModifyHostViaMonitoringPermissions($hostname) + { + if ($this->hasPermission('director/monitoring/hosts')) { + $monitoring = new Monitoring(); + return $monitoring->authCanEditHost($this->Auth(), $hostname); } + + return false; } /** diff --git a/application/controllers/ServiceController.php b/application/controllers/ServiceController.php index 1cd7db444..b51f38f79 100644 --- a/application/controllers/ServiceController.php +++ b/application/controllers/ServiceController.php @@ -4,6 +4,7 @@ use Exception; use Icinga\Module\Director\Forms\IcingaServiceForm; +use Icinga\Module\Director\Monitoring; use Icinga\Module\Director\Web\Controller\ObjectController; use Icinga\Module\Director\Objects\IcingaServiceSet; use Icinga\Module\Director\Objects\IcingaService; @@ -25,6 +26,10 @@ class ServiceController extends ObjectController protected function checkDirectorPermissions() { + if ($this->hasPermission('director/monitoring/services')) { + $monitoring = new Monitoring(); + return $monitoring->authCanEditService($this->Auth(), $this->getParam('host'), $this->getParam('name')); + } $this->assertPermission('director/hosts'); } diff --git a/configuration.php b/configuration.php index bc089c255..f2167a678 100644 --- a/configuration.php +++ b/configuration.php @@ -33,6 +33,14 @@ 'director/monitoring/services-ro', $this->translate('Allow readonly users to see where a Service came from') ); +$this->providePermission( + 'director/monitoring/hosts', + $this->translate('Allow users to modify Hosts they are allowed to see in the monitoring module') +); +$this->providePermission( + 'director/monitoring/services', + $this->translate('Allow users to modify Service they are allowed to see in the monitoring module') +); $this->providePermission('director/*', $this->translate('Allow unrestricted access to Icinga Director')); $this->provideRestriction( @@ -41,6 +49,14 @@ 'Limit access to the given comma-separated list of hostgroups' ) ); + +$this->provideRestriction( + 'director/monitoring/rw-object-filter', + $this->translate( + 'Additional (monitoring module) object filter to further restrict write access' + ) +); + $this->providePermission( 'director/groups-for-restricted-hosts', $this->translate('Allow users with Hostgroup restrictions to access the Groups field') diff --git a/doc/82-Changelog.md b/doc/82-Changelog.md index fd38cfa85..48497810b 100644 --- a/doc/82-Changelog.md +++ b/doc/82-Changelog.md @@ -14,6 +14,9 @@ next (will be 1.9.0) ### Import and Sync * FEATURE: introduce 'disable' as your purge action on Sync (#2285) +### Permissions and Restrictions +* FEATURE: allow using monitoring module permissions (#2304) + ### User Interface * FIX: allow switching DB config while connection is failing (#2300) * FIX: show Override button when all Fields belong to Field Categories (#2303) diff --git a/library/Director/Monitoring.php b/library/Director/Monitoring.php index 612a715b5..e86ea5bdf 100644 --- a/library/Director/Monitoring.php +++ b/library/Director/Monitoring.php @@ -3,6 +3,8 @@ namespace Icinga\Module\Director; use Icinga\Application\Icinga; +use Icinga\Authentication\Auth; +use Icinga\Data\Filter\Filter; use Icinga\Module\Monitoring\Backend\MonitoringBackend; class Monitoring @@ -29,43 +31,115 @@ public function isAvailable() public function hasHost($hostname) { - return $this->backend->select()->from('hostStatus', array( + return $this->backend->select()->from('hostStatus', [ 'hostname' => 'host_name', - ))->where('host_name', $hostname)->fetchOne() === $hostname; + ])->where('host_name', $hostname)->fetchOne() === $hostname; + } + + public function hasService($hostname, $service) + { + return (array) $this->prepareServiceKeyColumnQuery($hostname, $service)->fetchRow() === [ + 'hostname' => $hostname, + 'service' => $service, + ]; + } + + public function authCanEditHost(Auth $auth, $hostname) + { + if ($auth->hasPermission('director/monitoring/hosts')) { + $restriction = null; + foreach ($auth->getRestrictions('director/monitoring/rw-object-filter') as $restriction) { + if ($this->hasHostWithExtraFilter($hostname, Filter::fromQueryString($restriction))) { + return true; + } + } + if ($restriction === null) { + return $this->hasHost($hostname); + } + } + + return false; + } + + public function authCanEditService(Auth $auth, $hostname, $service) + { + if ($auth->hasPermission('director/monitoring/services')) { + $restriction = null; + foreach ($auth->getRestrictions('director/monitoring/rw-object-filter') as $restriction) { + if ($this->hasServiceWithExtraFilter($hostname, $service, Filter::fromQueryString($restriction))) { + return true; + } + } + if ($restriction === null) { + return $this->hasService($hostname, $service); + } + } + + return false; + } + + public function hasHostWithExtraFilter($hostname, Filter $filter) + { + return $this->backend->select()->from('hostStatus', [ + 'hostname' => 'host_name', + ])->where('host_name', $hostname)->applyFilter($filter)->fetchOne() === $hostname; + } + + public function hasServiceWithExtraFilter($hostname, $service, Filter $filter) + { + return (array) $this + ->prepareServiceKeyColumnQuery($hostname, $service) + ->applyFilter($filter) + ->fetchRow() === [ + 'hostname' => $hostname, + 'service' => $service, + ]; } public function getHostState($hostname) { - $hostStates = array( + $hostStates = [ '0' => 'up', '1' => 'down', '2' => 'unreachable', '99' => 'pending', - ); + ]; - $query = $this->backend->select()->from('hostStatus', array( + $query = $this->backend->select()->from('hostStatus', [ 'hostname' => 'host_name', 'state' => 'host_state', 'problem' => 'host_problem', 'acknowledged' => 'host_acknowledged', 'in_downtime' => 'host_in_downtime', 'output' => 'host_output', - ))->where('host_name', $hostname); + ])->where('host_name', $hostname); $res = $query->fetchRow(); if ($res === false) { - $res = (object) array( + $res = (object) [ 'hostname' => $hostname, 'state' => '99', 'problem' => '0', 'acknowledged' => '0', 'in_downtime' => '0', 'output' => null, - ); + ]; } $res->state = $hostStates[$res->state]; return $res; } + + protected function prepareServiceKeyColumnQuery($hostname, $service) + { + return $this->backend + ->select() + ->from('serviceStatus', [ + 'hostname' => 'host_name', + 'service' => 'service_description', + ]) + ->where('host_name', $hostname) + ->where('service_description', $service); + } } diff --git a/library/Director/ProvidedHook/Monitoring/HostActions.php b/library/Director/ProvidedHook/Monitoring/HostActions.php index 6e72daea3..cec197c95 100644 --- a/library/Director/ProvidedHook/Monitoring/HostActions.php +++ b/library/Director/ProvidedHook/Monitoring/HostActions.php @@ -4,7 +4,9 @@ use Exception; use Icinga\Application\Config; +use Icinga\Authentication\Auth; use Icinga\Module\Director\Db; +use Icinga\Module\Director\Monitoring; use Icinga\Module\Director\Objects\IcingaHost; use Icinga\Module\Director\Util; use Icinga\Module\Monitoring\Hook\HostActionsHook; @@ -37,7 +39,19 @@ protected function getThem(Host $host) ); } + $allowEdit = false; if (Util::hasPermission('director/hosts') && IcingaHost::exists($hostname, $db)) { + $allowEdit = true; + } + $auth = Auth::getInstance(); + if (Util::hasPermission('director/monitoring/hosts')) { + $monitoring = new Monitoring(); + if ($monitoring->authCanEditHost($auth, $hostname)) { + $allowEdit = IcingaHost::exists($hostname, $db); + } + } + + if ($allowEdit) { $actions[mt('director', 'Modify')] = Url::fromPath( 'director/host/edit', array('name' => $hostname) diff --git a/library/Director/ProvidedHook/Monitoring/ServiceActions.php b/library/Director/ProvidedHook/Monitoring/ServiceActions.php index f45662b5a..a3d991af1 100644 --- a/library/Director/ProvidedHook/Monitoring/ServiceActions.php +++ b/library/Director/ProvidedHook/Monitoring/ServiceActions.php @@ -4,7 +4,9 @@ use Exception; use Icinga\Application\Config; +use Icinga\Authentication\Auth; use Icinga\Module\Director\Db; +use Icinga\Module\Director\Monitoring; use Icinga\Module\Director\Objects\IcingaHost; use Icinga\Module\Director\Util; use Icinga\Module\Monitoring\Hook\ServiceActionsHook; @@ -36,6 +38,7 @@ protected function getThem(Service $service) } $hostname = $service->host_name; + $serviceName = $service->service_description; if (Util::hasPermission('director/inspect')) { $actions[mt('director', 'Inspect')] = Url::fromPath('director/inspect/object', [ 'type' => 'service', @@ -43,23 +46,27 @@ protected function getThem(Service $service) 'name' => sprintf( '%s!%s', $hostname, - $service->service_description + $serviceName ) ]); } + $title = null; if (Util::hasPermission('director/hosts')) { $title = mt('director', 'Modify'); + } elseif (Util::hasPermission('director/monitoring/services')) { + $monitoring = new Monitoring(); + if ($monitoring->authCanEditService(Auth::getInstance(), $hostname, $serviceName)) { + $title = mt('director', 'Modify'); + } } elseif (Util::hasPermission('director/monitoring/services-ro')) { $title = mt('director', 'Configuration'); - } else { - return $actions; } - if (IcingaHost::exists($hostname, $db)) { + if ($title && IcingaHost::exists($hostname, $db)) { $actions[$title] = Url::fromPath('director/host/findservice', [ 'name' => $hostname, - 'service' => $service->service_description + 'service' => $serviceName ]); } diff --git a/library/Director/Web/Tabs/ObjectTabs.php b/library/Director/Web/Tabs/ObjectTabs.php index 11c5bfd87..cbd3f1535 100644 --- a/library/Director/Web/Tabs/ObjectTabs.php +++ b/library/Director/Web/Tabs/ObjectTabs.php @@ -138,7 +138,7 @@ protected function addTabsForExistingObject() ]); } - if ($object->getShortTableName() === 'host') { + if ($object->getShortTableName() === 'host' && $auth->hasPermission('director/hosts')) { $this->add('agent', [ 'url' => 'director/host/agent', 'urlParams' => $params,