From 23c1097577a172ac81e5084585cb30cb8c9ef2c3 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 30 Sep 2014 10:11:40 +0200 Subject: [PATCH 01/62] Fix showing a contact throwing an exception --- modules/monitoring/application/controllers/ShowController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index 8a399c250f..129903ce04 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -37,7 +37,7 @@ class Monitoring_ShowController extends Controller public function init() { $this->view->object = MonitoredObject::fromParams($this->params); - if ($this->view->object->fetch() === false) { + if ($this->view->object && $this->view->object->fetch() === false) { throw new Zend_Controller_Action_Exception($this->translate('Host or service not found')); } From ac2aeca9e6d5071513ede938ce543ab7ff716a4f Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 30 Sep 2014 11:34:04 +0200 Subject: [PATCH 02/62] Conform StatehistoryForm to new Form implementation --- .../application/controllers/ListController.php | 10 +++------- .../monitoring/application/forms/StatehistoryForm.php | 8 ++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 5f50eda130..d3427e6a49 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -370,8 +370,8 @@ public function statehistorysummaryAction() $form->setEnctype(Zend_Form::ENCTYPE_URLENCODED); $form->setMethod('get'); $form->setTokenDisabled(); - $form->setRequest($this->getRequest()); - $form->buildForm(); + $form->setUidDisabled(); + $form->render(); $this->view->form = $form; $orientation = $this->params->shift('horizontal', 0) ? 'horizontal' : 'vertical'; @@ -391,11 +391,7 @@ public function statehistorysummaryAction() 'stateHistorySummary', array('day', $form->getValue('state')) ); - $this->params->shift('objecttype'); - $this->params->shift('from'); - $this->params->shift('to'); - $this->params->shift('state'); - $this->params->shift('btn_submit'); + $this->params->remove(array('objecttype', 'from', 'to', 'state', 'btn_submit')); $this->applyFilters($query); $this->view->summary = $query->getQuery()->fetchAll(); $this->view->column = $form->getValue('state'); diff --git a/modules/monitoring/application/forms/StatehistoryForm.php b/modules/monitoring/application/forms/StatehistoryForm.php index d4219bfd98..ed5710454c 100644 --- a/modules/monitoring/application/forms/StatehistoryForm.php +++ b/modules/monitoring/application/forms/StatehistoryForm.php @@ -48,11 +48,9 @@ public function getFilter() } /** - * Create the confirmation form - * - * @see Form::create() + * @see Form::createElements() */ - public function create() + public function createElements(array $formData) { $this->addElement( 'select', @@ -137,8 +135,6 @@ public function create() ) ); } - - $this->enableAutoSubmit(array('from', 'objecttype', 'state')); $this->addElement( 'button', 'btn_submit', From e6c674e221174eaf5f8e25324c185eed7e07c0d7 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 30 Sep 2014 14:47:17 +0200 Subject: [PATCH 03/62] MonitoredObject: Implement awesome __isset() --- .../library/Monitoring/Object/MonitoredObject.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 7b56929c8b..3d3bde8dde 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -442,7 +442,7 @@ public function __get($name) { if (property_exists($this->properties, $name)) { return $this->properties->$name; - } elseif (isset($this->$name)) { + } elseif (property_exists($this, $name) && $this->$name !== null) { return $this->$name; } elseif (property_exists($this, $name)) { $fetchMethod = 'fetch' . ucfirst($name); @@ -458,6 +458,16 @@ public function __get($name) throw new InvalidPropertyException('Can\'t access property \'%s\'. Property does not exist.', $name); } + public function __isset($name) + { + if (property_exists($this->properties, $name)) { + return isset($this->properties->$name); + } elseif (property_exists($this, $name)) { + return isset($this->$name); + } + return false; + } + /** * @deprecated */ From 9a57ddcfa61699c5ba77bb614a27a292b7a9a57c Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 30 Sep 2014 15:49:31 +0200 Subject: [PATCH 04/62] Fix login redirects when AutologinBackend is used --- application/controllers/AuthenticationController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index b877796696..31e7d271f9 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -124,7 +124,9 @@ public function loginAction() $authenticated = $backend->authenticate($user); if ($authenticated === true) { $auth->setAuthenticated($user); - $this->rerenderLayout()->redirectNow($redirectUrl); + $this->rerenderLayout()->redirectNow( + Url::fromPath(Url::fromRequest()->getParam('redirect', 'dashboard')) + ); } } } From f1903855b9fa578c4c7e1ae2fa537ad7ebfb5ab0 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 30 Sep 2014 15:50:16 +0200 Subject: [PATCH 05/62] Avoid type warning in LoginForm --- application/forms/Authentication/LoginForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/Authentication/LoginForm.php b/application/forms/Authentication/LoginForm.php index d878475f28..0afc567492 100644 --- a/application/forms/Authentication/LoginForm.php +++ b/application/forms/Authentication/LoginForm.php @@ -24,7 +24,7 @@ public function init() /** * @see Form::createElements() */ - public function createElements($formData) + public function createElements(array $formData) { $this->addElement( 'text', From 8eb7db9b3575a829f97c892b72f4483e7582b890 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 30 Sep 2014 15:50:58 +0200 Subject: [PATCH 06/62] Avoid type warning when using array_combine on empty arrays --- library/Icinga/User.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Icinga/User.php b/library/Icinga/User.php index 03aa1bcd23..659767644e 100644 --- a/library/Icinga/User.php +++ b/library/Icinga/User.php @@ -198,7 +198,9 @@ public function getPermissions() public function setPermissions(array $permissions) { natcasesort($permissions); - $this->permissions = array_combine($permissions, $permissions); + if (! empty($permissions)) { + $this->permissions = array_combine($permissions, $permissions); + } return $this; } From 8008387559a00b5280b172b109852b308343d351 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Tue, 30 Sep 2014 15:59:11 +0200 Subject: [PATCH 07/62] Fix typo --- application/controllers/AuthenticationController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index 31e7d271f9..bc88ef7d11 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -111,7 +111,7 @@ public function loginAction() if ($backendsWithError) { $this->view->form->getElement('username')->addError( $this->translate( - 'Please note that not all authentication methods where available.' + 'Please note that not all authentication methods were available.' . ' Check the system log or Icinga Web 2 log for more information.' ) ); From 1c7eb0d59acc023769326b8a2f751e5aedaf91fe Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 30 Sep 2014 22:34:58 +0200 Subject: [PATCH 08/62] lib: Introduce function `String::cname()' --- library/Icinga/Util/String.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/Icinga/Util/String.php b/library/Icinga/Util/String.php index b64d9e5240..0bebb06e73 100644 --- a/library/Icinga/Util/String.php +++ b/library/Icinga/Util/String.php @@ -21,4 +21,18 @@ public static function trimSplit($value, $delimiter = ',') { return array_map('trim', explode($delimiter, $value)); } + + /** + * Uppercase the first character of each word in a string assuming and removing the underscore as word separator + * + * Converts 'first_name' to 'firstName' for example. + * + * @param string $name + * + * @return string + */ + public static function cname($name) + { + return str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name)))); + } } From 46078f50db4b1784615a2054fd474397044e613d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 03:13:27 +0200 Subject: [PATCH 09/62] filter: Fix that leading and trailing whitespaces for columns and expressions let filters fail Before, filtering for "host = localhost" issued "got invalid column host". --- library/Icinga/Data/Filter/Filter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/Icinga/Data/Filter/Filter.php b/library/Icinga/Data/Filter/Filter.php index 64607a4a0c..8a063ee2a6 100644 --- a/library/Icinga/Data/Filter/Filter.php +++ b/library/Icinga/Data/Filter/Filter.php @@ -131,6 +131,8 @@ public static function where($col, $filter) public static function expression($col, $op, $expression) { + $col = trim($col); + $expression = trim($expression); switch ($op) { case '=': return new FilterMatch($col, $op, $expression); case '<': return new FilterLessThan($col, $op, $expression); From 56a1af47e7ff73352c6ffb105ef88845f5f7bed4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 03:23:06 +0200 Subject: [PATCH 10/62] filter: Add test for whitespace sanitizing --- test/php/library/Icinga/Data/Filter/FilterTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/php/library/Icinga/Data/Filter/FilterTest.php b/test/php/library/Icinga/Data/Filter/FilterTest.php index dbd640b91e..b8f9ea78a9 100644 --- a/test/php/library/Icinga/Data/Filter/FilterTest.php +++ b/test/php/library/Icinga/Data/Filter/FilterTest.php @@ -199,6 +199,16 @@ public function testCloningDeepFilters() $this->assertNotEquals((string) $c, (string) $d); } + public function testLeadingAndTrailingWhitespacesSanitizing() + { + $columnHasWhitespaces = Filter::where(' host ', 'localhost'); + $expressionHasWhitespaces = Filter::where('host', ' localhost '); + $bothHaveWhitespaces = Filter::fromQueryString(' host = localhost '); + $this->assertTrue($columnHasWhitespaces->matches($this->sampleData[0])); + $this->assertTrue($expressionHasWhitespaces->matches($this->sampleData[0])); + $this->assertTrue($bothHaveWhitespaces->matches($this->sampleData[0])); + } + private function row($idx) { return $this->sampleData[$idx]; From ebde4228243a4a929977e5f9559f1775f222c1d3 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 04:00:43 +0200 Subject: [PATCH 11/62] filter: Fix whitepsace sanitation when expression is an array --- library/Icinga/Data/Filter/Filter.php | 2 +- test/php/library/Icinga/Data/Filter/FilterTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Data/Filter/Filter.php b/library/Icinga/Data/Filter/Filter.php index 8a063ee2a6..866d161b83 100644 --- a/library/Icinga/Data/Filter/Filter.php +++ b/library/Icinga/Data/Filter/Filter.php @@ -132,7 +132,7 @@ public static function where($col, $filter) public static function expression($col, $op, $expression) { $col = trim($col); - $expression = trim($expression); + $expression = is_array($expression) ? array_map('trim', $expression) : trim($expression); switch ($op) { case '=': return new FilterMatch($col, $op, $expression); case '<': return new FilterLessThan($col, $op, $expression); diff --git a/test/php/library/Icinga/Data/Filter/FilterTest.php b/test/php/library/Icinga/Data/Filter/FilterTest.php index b8f9ea78a9..7d63492137 100644 --- a/test/php/library/Icinga/Data/Filter/FilterTest.php +++ b/test/php/library/Icinga/Data/Filter/FilterTest.php @@ -204,9 +204,11 @@ public function testLeadingAndTrailingWhitespacesSanitizing() $columnHasWhitespaces = Filter::where(' host ', 'localhost'); $expressionHasWhitespaces = Filter::where('host', ' localhost '); $bothHaveWhitespaces = Filter::fromQueryString(' host = localhost '); + $withArray = Filter::where(' host ', array(' no match ', ' localhost ')); $this->assertTrue($columnHasWhitespaces->matches($this->sampleData[0])); $this->assertTrue($expressionHasWhitespaces->matches($this->sampleData[0])); $this->assertTrue($bothHaveWhitespaces->matches($this->sampleData[0])); + $this->assertTrue($withArray->matches($this->sampleData[0])); } private function row($idx) From 266936b9b5dc20f0b608a445297da767062baff4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 08:13:17 +0200 Subject: [PATCH 12/62] Fix that logout of unauthenticated users throws exceptions --- application/controllers/AuthenticationController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index bc88ef7d11..c5b0e929c2 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -142,14 +142,16 @@ public function loginAction() public function logoutAction() { $auth = $this->Auth(); + if (! $auth->isAuthenticated()) { + $this->redirectToLogin(); + } $isRemoteUser = $auth->getUser()->isRemoteUser(); $auth->removeAuthorization(); - if ($isRemoteUser === true) { $this->_helper->layout->setLayout('login'); $this->_response->setHttpResponseCode(401); } else { - $this->rerenderLayout()->redirectToLogin(); + $this->redirectToLogin(); } } } From 084691570e8ef5f1c2d6dee5a6c58d3ec87636b7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 08:14:03 +0200 Subject: [PATCH 13/62] permissions: Use a comma-separated list as config instead of the `permission_*' directives Permissions are now set using a comma-separated list of permissions using the `permissions' config because the `users' and `groups' are comma-separated lists too. --- library/Icinga/Authentication/AdmissionLoader.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Authentication/AdmissionLoader.php b/library/Icinga/Authentication/AdmissionLoader.php index f961fb87c2..28485d464e 100644 --- a/library/Icinga/Authentication/AdmissionLoader.php +++ b/library/Icinga/Authentication/AdmissionLoader.php @@ -52,12 +52,8 @@ public function getPermissions($username, array $groups) return $permissions; } foreach ($config as $section) { - if ($this->match($section, $username, $groups)) { - foreach ($section as $key => $value) { - if (strpos($key, 'permission') === 0) { - $permissions = array_merge($permissions, String::trimSplit($value)); - } - } + if ($this->match($section, $username, $groups) && isset($section->permissions)) { + $permissions += String::trimSplit($section->permissions); } } return $permissions; From fa9bd5956557cf3ad249d82f70a78d72818a9716 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 08:17:35 +0200 Subject: [PATCH 14/62] lib: Support `isset()' and `empty()' checks on CLI params --- library/Icinga/Cli/Params.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/Icinga/Cli/Params.php b/library/Icinga/Cli/Params.php index 7236dcca24..5c6cbfe8b1 100644 --- a/library/Icinga/Cli/Params.php +++ b/library/Icinga/Cli/Params.php @@ -108,6 +108,18 @@ public function getAllStandalone() return $this->standalone; } + /** + * Support isset() and empty() checks on options + * + * @param $name + * + * @return bool + */ + public function __isset($name) + { + return isset($this->params[$name]); + } + /** * @see Params::get() */ From bbee06b57fed11e80ac12eba775f58824add28ec Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 08:18:34 +0200 Subject: [PATCH 15/62] form: Ignore note elments when retrieving values at form level --- library/Icinga/Web/Form/Element/Note.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Web/Form/Element/Note.php b/library/Icinga/Web/Form/Element/Note.php index 700bea1075..5dd489f0d6 100644 --- a/library/Icinga/Web/Form/Element/Note.php +++ b/library/Icinga/Web/Form/Element/Note.php @@ -7,7 +7,7 @@ use Zend_Form_Element; /** - * Implements note element for Zend forms + * A note */ class Note extends Zend_Form_Element { @@ -23,17 +23,25 @@ class Note extends Zend_Form_Element protected $_disableLoadDefaultDecorators = true; /** - * Name of the view helper + * Form view helper to use for rendering * * @var string */ public $helper = 'formNote'; /** - * Return true to ensure redrawing + * Ignore element when retrieving values at form level * - * @param mixed $value The value of to validate (ignored) - * @return bool Always true + * @var bool + */ + protected $_ignore = true; + + /** + * Validate element value (pseudo) + * + * @param mixed $value Ignored + * + * @return bool Always true */ public function isValid($value) { From f188bf6b9cbe279bb4b822683df2ead0e8636c97 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 1 Oct 2014 09:24:52 +0200 Subject: [PATCH 16/62] Make "hosts" and "services" views responsive --- .../controllers/HostsController.php | 7 +++ .../controllers/ServicesController.php | 7 +++ .../views/scripts/hosts/show.phtml | 41 ++++++------ .../views/scripts/services/show.phtml | 62 +++++++++---------- public/css/icinga/layout-structure.less | 18 ++++++ 5 files changed, 78 insertions(+), 57 deletions(-) diff --git a/modules/monitoring/application/controllers/HostsController.php b/modules/monitoring/application/controllers/HostsController.php index 8298f456b1..48b0687051 100644 --- a/modules/monitoring/application/controllers/HostsController.php +++ b/modules/monitoring/application/controllers/HostsController.php @@ -43,6 +43,13 @@ protected function handleCommandForm(ObjectsCommandForm $form) public function showAction() { + $this->getTabs()->add( + 'show', + array( + 'title' => t('Hosts'), + 'url' => Url::fromRequest() + ) + )->activate('show'); $this->setAutorefreshInterval(15); $checkNowForm = new CheckNowCommandForm(); $checkNowForm diff --git a/modules/monitoring/application/controllers/ServicesController.php b/modules/monitoring/application/controllers/ServicesController.php index 633ff521b2..c421ed8814 100644 --- a/modules/monitoring/application/controllers/ServicesController.php +++ b/modules/monitoring/application/controllers/ServicesController.php @@ -43,6 +43,13 @@ protected function handleCommandForm(ObjectsCommandForm $form) public function showAction() { + $this->getTabs()->add( + 'show', + array( + 'title' => t('Services'), + 'url' => Url::fromRequest() + ) + )->activate('show'); $this->setAutorefreshInterval(15); $checkNowForm = new CheckNowCommandForm(); $checkNowForm diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index 14d160c1ea..e43198780d 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -5,26 +5,23 @@ translate('No hosts matching the filter') ?> -

translate('Summary For %u Hosts'), count($objects)) ?>

- - - - - - - - -
translate('%u Hosts'), array_sum(array_values($hostStates))) ?>
- hostStatesPieChart ?> - - $count) { - echo sprintf('%s: %u
', strtoupper($text), $count); - } +
+ translate('Hosts (%u)'), array_sum(array_values($hostStates))) ?> +
+
+ hostStatesPieChart ?> +
+
+ $count) { + echo sprintf('%s: %u
', strtoupper($text), $count); + } ?> +
- ?> -
+

+ translate('%u Hosts'), + count($objects)) + ?> +

-
-
@@ -53,7 +48,7 @@ -

+

translatePlural( '%u Unhandled Host Problem', @@ -62,7 +57,7 @@ ), count($unhandledObjects) ) ?> -

+
diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index 4d5d46e056..802aa8ef62 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -5,39 +5,34 @@ translate('No services matching the filter') ?> -

translate('Summary For %u Services'), count($objects)) ?>

- - - - - - - - - - - -
translate('%u Services'), array_sum(array_values($serviceStates))) ?>translate('%u Hosts'), array_sum(array_values($hostStates))) ?>
- serviceStatesPieChart ?> - - $count) { - echo sprintf('%s: %u
', strtoupper($text), $count); - } - - ?> -
- hostStatesPieChart ?> - - $count) { - echo sprintf('%s: %u
', strtoupper($text), $count); - } +
+
+ translate('Services (%u)'), array_sum(array_values($serviceStates))) ?> +
+
+ serviceStatesPieChart ?> +
+
+ $count) { + echo sprintf(' %s: %u
', strtoupper($text), $count); + } ?> +
+
- ?> -
+
+
+ translate('Hosts (%u)'), array_sum(array_values($hostStates))) ?> +
+
+ hostStatesPieChart ?> +
+
+ $count) { + echo sprintf('%s: %u
', strtoupper($text), $count); + } ?> +
+
-
@@ -66,7 +60,7 @@
-

+

translatePlural( '%u Unhandled Service Problem', @@ -75,7 +69,7 @@ ), count($unhandledObjects) ) ?> -

+
diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 6319e65d1a..be8a8703f4 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -331,3 +331,21 @@ html { font-weight: bold; color: white; } + +.hbox { + display: inline-block; +} + +.hbox-item { + display: inline-block; + vertical-align: top; + margin-bottom: 0.25em; + margin-left: 1em; + margin-right: 1em; +} + +.hbox-spacer { + display: inline-block; + vertical-align: top; + width: 2em; +} From 64d41ac5a30628ae1aa6b9d0ad91ad4e6e2c1e6b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 12:50:48 +0200 Subject: [PATCH 17/62] filter: Make `DbQuery::applyFilterSql()' public I want to use that function in a module :) --- library/Icinga/Data/Db/DbQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index b701f3ac07..a759da6b54 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -117,7 +117,7 @@ public function getSelectQuery() return $select; } - protected function applyFilterSql($query) + public function applyFilterSql($query) { $where = $this->renderFilter($this->filter); if ($where !== '') { From 74bd9b319d46a54f360583f5f780cb73bfb1c673 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 14:08:21 +0200 Subject: [PATCH 18/62] restrictions: Include restriction's section name in user restrictions --- library/Icinga/Authentication/AdmissionLoader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Authentication/AdmissionLoader.php b/library/Icinga/Authentication/AdmissionLoader.php index 28485d464e..098afda815 100644 --- a/library/Icinga/Authentication/AdmissionLoader.php +++ b/library/Icinga/Authentication/AdmissionLoader.php @@ -75,12 +75,12 @@ public function getRestrictions($username, array $groups) } catch (NotReadableError $e) { return $restrictions; } - foreach ($config as $section) { + foreach ($config as $name => $section) { if ($this->match($section, $username, $groups)) { if (!array_key_exists($section->name, $restrictions)) { $restrictions[$section->name] = array(); } - $restrictions[$section->name][] = $section->restriction; + $restrictions[$section->name][$name] = $section->restriction; } } return $restrictions; From 531448eee40d3753f3d95871beb8d230ac56f61d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Oct 2014 14:17:29 +0200 Subject: [PATCH 19/62] monitoring: Fix setting a resource on a backend The resources were indexed by integers instead of the resource names. --- .../monitoring/application/forms/Config/BackendConfigForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index e2cdf210cb..fa05cf7e3a 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -46,7 +46,7 @@ public function setResourceConfig(Config $resourceConfig) $resources = array(); foreach ($resourceConfig as $name => $resource) { if ($resource->type === 'db' || $resource->type === 'statusdat' || $resource->type === 'livestatus') { - $resources[$resource->type === 'db' ? 'ido' : strtolower($resource->type)][] = $name; + $resources[$resource->type === 'db' ? 'ido' : strtolower($resource->type)][$name] = $name; } } From ed91e119d2378d58dcd9d0ecc1b3ced3c8948a47 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 1 Oct 2014 14:51:51 +0200 Subject: [PATCH 20/62] Add close-button to all containers refs #6216 --- library/Icinga/Web/Widget/Tabs.php | 34 ++++++++++++++++++++++++++++++ public/css/icinga/tabs.less | 3 +++ public/js/icinga/events.js | 18 ++++++++++++++++ public/js/icinga/history.js | 1 + public/js/icinga/ui.js | 28 ++++++++++++++++++++++-- 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index 716d5a9596..d79453a016 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -23,6 +23,7 @@ class Tabs extends AbstractWidget implements Countable
    {TABS} {DROPDOWN} + {CLOSE}
EOT; @@ -40,6 +41,18 @@ class Tabs extends AbstractWidget implements Countable EOT; + /** + * Template used for the close-button + * + * @var string + */ + private $closeTpl = <<< 'EOT' +
+EOT; + + /** * This is where single tabs added to this container will be stored * @@ -61,6 +74,21 @@ class Tabs extends AbstractWidget implements Countable */ private $dropdownTabs = array(); + /** + * Whether the tabs should contain a close-button + * + * @var bool + */ + private $closeTab = true; + + /** + * Set whether the current tab is closable + */ + public function hideCloseButton() + { + $this->closeTab = false; + } + /** * Activate the tab with the given name * @@ -235,6 +263,11 @@ private function renderTabs() return $tabs; } + private function renderCloseTab() + { + return $this->closeTpl; + } + /** * Render to HTML * @@ -249,6 +282,7 @@ public function render() $html = $this->baseTpl; $html = str_replace('{TABS}', $this->renderTabs(), $html); $html = str_replace('{DROPDOWN}', $this->renderDropdownTabs(), $html); + $html = str_replace('{CLOSE}', $this->closeTab ? $this->renderCloseTab() : '', $html); return $html; } diff --git a/public/css/icinga/tabs.less b/public/css/icinga/tabs.less index ed846fc9dc..74e3e0b72b 100644 --- a/public/css/icinga/tabs.less +++ b/public/css/icinga/tabs.less @@ -136,3 +136,6 @@ ul.tabs img.icon { margin-top: -4px; } +a.close-tab { + display: none; +} \ No newline at end of file diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 9c889dfa8d..5f62a8c8a7 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -83,6 +83,12 @@ if (searchField.length && searchField.val().length) { self.searchValue = searchField.val(); } + + if (icinga.ui.isOneColLayout()) { + icinga.ui.disableCloseButtons(); + } else { + icinga.ui.enableCloseButtons(); + } }, /** @@ -371,6 +377,18 @@ // If link has hash tag... if (href.match(/#/)) { if (href === '#') { + if ($a.hasClass('close-toggle')) { + if (! icinga.ui.isOneColLayout()) { + var $cont = $a.closest('.container').first(); + if ($cont.attr('id') === 'col1') { + icinga.ui.moveToLeft(); + icinga.ui.layout1col(); + } else { + icinga.ui.layout1col(); + } + icinga.history.pushCurrentState(); + } + } return false; } $target = self.getLinkTargetFor($a); diff --git a/public/js/icinga/history.js b/public/js/icinga/history.js index a3fcf5deb3..9f05def2d3 100644 --- a/public/js/icinga/history.js +++ b/public/js/icinga/history.js @@ -90,6 +90,7 @@ } }); + // TODO: update navigation // Did we find any URL? Then push it! if (url !== '') { window.history.pushState({icinga: true}, null, this.cleanupUrl(url)); diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index 28ce353e18..7f2507bdc7 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -199,6 +199,11 @@ self.refreshDebug(); }, + /** + * Returns whether the layout is too small for more than one column + * + * @returns {boolean} True when more than one column is available + */ hasOnlyOneColumn: function () { return this.currentLayout === 'poor' || this.currentLayout === 'minimal'; }, @@ -229,11 +234,21 @@ return false; }, + /** + * Returns whether only one column is displayed + * + * @returns {boolean} True when only one column is displayed + */ + isOneColLayout: function () { + return ! $('#layout').hasClass('twocols'); + }, + layout1col: function () { - if (! $('#layout').hasClass('twocols')) { return; } + if (this.isOneColLayout()) { return; } this.icinga.logger.debug('Switching to single col'); $('#layout').removeClass('twocols'); this.closeContainer($('#col2')); + this.disableCloseButtons(); }, closeContainer: function($c) { @@ -247,10 +262,11 @@ }, layout2col: function () { - if ($('#layout').hasClass('twocols')) { return; } + if (! this.isOneColLayout()) { return; } this.icinga.logger.debug('Switching to double col'); $('#layout').addClass('twocols'); this.fixControls(); + this.enableCloseButtons(); }, getAvailableColumnSpace: function () { @@ -698,6 +714,14 @@ this.fixControls(parent); }, + disableCloseButtons: function () { + $('a.close-toggle').hide(); + }, + + enableCloseButtons: function () { + $('a.close-toggle').show(); + }, + fixControls: function ($parent) { var self = this; From 017d4b8c9deb17971bff5b8aa06959e9df43aa50 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Wed, 1 Oct 2014 15:58:53 +0200 Subject: [PATCH 21/62] Introduce Groups from LDAP to User Object --- config/authentication.ini.in | 4 + .../Backend/LdapUserBackend.php | 51 ++++++++++- library/Icinga/Authentication/UserBackend.php | 84 +++++++++++++++---- 3 files changed, 121 insertions(+), 18 deletions(-) diff --git a/config/authentication.ini.in b/config/authentication.ini.in index 71d9e4402a..2a2d2a9693 100644 --- a/config/authentication.ini.in +++ b/config/authentication.ini.in @@ -18,6 +18,10 @@ backend = ldap resource = internal_ldap user_class = @ldap_user_objectclass@ user_name_attribute = @ldap_attribute_username@ +group_base_dn = @ldap_group_base_dn@ +group_attribute = @ldap_group_attribute@ +group_member_attribute = @ldap_group_member_attribute@ +group_class = @ldap_group_class@ [internal_db_authentication] @internal_auth_disabled@ diff --git a/library/Icinga/Authentication/Backend/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php index 23a79781d5..247f638537 100644 --- a/library/Icinga/Authentication/Backend/LdapUserBackend.php +++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php @@ -4,6 +4,7 @@ namespace Icinga\Authentication\Backend; +use Icinga\Logger\Logger; use Icinga\User; use Icinga\Authentication\UserBackend; use Icinga\Protocol\Ldap\Connection; @@ -23,11 +24,14 @@ class LdapUserBackend extends UserBackend protected $userNameAttribute; - public function __construct(Connection $conn, $userClass, $userNameAttribute) + protected $groupOptions; + + public function __construct(Connection $conn, $userClass, $userNameAttribute, $groupOptions = null) { $this->conn = $conn; $this->userClass = $userClass; $this->userNameAttribute = $userNameAttribute; + $this->groupOptions = $groupOptions; } /** @@ -83,6 +87,41 @@ public function assertAuthenticationPossible() } } + /** + * Retrieve the user groups + * + * @param string $dn + * + * @return array|null + */ + public function getGroups($dn) + { + if (empty($this->groupOptions)) { + return null; + } + + $q = $this->conn->select() + ->setBase($this->groupOptions['group_base_dn']) + ->from( + $this->groupOptions['group_class'], + array($this->groupOptions['group_attribute']) + ) + ->where( + $this->groupOptions['group_member_attribute'], + $dn + ); + + $result = $this->conn->fetchAll($q); + + $groups = array(); + + foreach ($result as $group) { + $groups[] = $group->{$this->groupOptions['group_attribute']}; + } + + return $groups; + } + /** * Test whether the given user exists * @@ -127,10 +166,15 @@ public function authenticate(User $user, $password, $healthCheck = true) return false; } try { - return $this->conn->testCredentials( - $this->conn->fetchDN($this->createQuery($user->getUsername())), + $userDn = $this->conn->fetchDN($this->createQuery($user->getUsername())); + $authenticated = $this->conn->testCredentials( + $userDn, $password ); + if ($authenticated) { + $user->setGroups($this->getGroups($userDn)); + } + return $authenticated; } catch (LdapException $e) { // Error during authentication of this specific user throw new AuthenticationException( @@ -160,4 +204,3 @@ public function count() ); } } - diff --git a/library/Icinga/Authentication/UserBackend.php b/library/Icinga/Authentication/UserBackend.php index 9e7abd62a1..b475e9135d 100644 --- a/library/Icinga/Authentication/UserBackend.php +++ b/library/Icinga/Authentication/UserBackend.php @@ -93,26 +93,34 @@ public static function create($name, Zend_Config $backendConfig) $backend = new DbUserBackend($resource); break; case 'msldap': + self::checkLdapConfiguration($name, $backendConfig); + $groupOptions = array( + 'group_base_dn' => $backendConfig->group_base_dn, + 'group_attribute' => $backendConfig->group_attribute, + 'group_member_attribute' => $backendConfig->group_member_attribute, + 'group_class' => $backendConfig->group_class + ); $backend = new LdapUserBackend( $resource, $backendConfig->get('user_class', 'user'), - $backendConfig->get('user_name_attribute', 'sAMAccountName') + $backendConfig->get('user_name_attribute', 'sAMAccountName'), + $groupOptions ); break; case 'ldap': - if (($userClass = $backendConfig->user_class) === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the user_class directive', - $name - ); - } - if (($userNameAttribute = $backendConfig->user_name_attribute) === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the user_name_attribute directive', - $name - ); - } - $backend = new LdapUserBackend($resource, $userClass, $userNameAttribute); + self::checkLdapConfiguration($name, $backendConfig); + $groupOptions = array( + 'group_base_dn' => $backendConfig->group_base_dn, + 'group_attribute' => $backendConfig->group_attribute, + 'group_member_attribute' => $backendConfig->group_member_attribute, + 'group_class' => $backendConfig->group_class + ); + $backend = new LdapUserBackend( + $resource, + $backendConfig->user_class, + $backendConfig->user_name_attribute, + $groupOptions + ); break; default: throw new ConfigurationError( @@ -144,4 +152,52 @@ abstract public function hasUser(User $user); * @return bool */ abstract public function authenticate(User $user, $password); + + /** + * Checks the ldap configuration + * + * @param $name + * @param Zend_Config $backendConfig + * + * @throws \Icinga\Exception\ConfigurationError + */ + protected static function checkLdapConfiguration($name, Zend_Config $backendConfig) + { + if ($backendConfig->user_class === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the user_class directive', + $name + ); + } + if ($backendConfig->user_name_attribute === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the user_name_attribute directive', + $name + ); + } + if ($backendConfig->group_base_dn === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the group_base_dn directive', + $name + ); + } + if ($backendConfig->group_attribute === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the group_attribute directive', + $name + ); + } + if ($backendConfig->group_member_attribute === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the group_member_attribute directive', + $name + ); + } + if ($backendConfig->group_class === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the group_class directive', + $name + ); + } + } } From fa2c9e46edb44e2482a89315ddb0414aa40d2f58 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 1 Oct 2014 16:48:38 +0200 Subject: [PATCH 22/62] Cleanup comments and whitespaces --- public/js/icinga/behavior/navigation.js | 2 +- public/js/icinga/loader.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/public/js/icinga/behavior/navigation.js b/public/js/icinga/behavior/navigation.js index 90cda97138..67eaf69932 100644 --- a/public/js/icinga/behavior/navigation.js +++ b/public/js/icinga/behavior/navigation.js @@ -24,8 +24,8 @@ Navigation.prototype.onRendered = function(evt) { // get original source element of the rendered-event var el = evt.target; - // restore old menu state if (activeMenuId) { + // restore old menu state $('[role="navigation"] li.active', el).removeClass('active'); var $selectedMenu = $('#' + activeMenuId).addClass('active'); var $outerMenu = $selectedMenu.parent().closest('li'); diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 9c3feab6e3..ae5b4b6457 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -340,9 +340,7 @@ if (! req.autorefresh) { // TODO: Hook for response/url? var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]'); - var $matches = $.merge($('[href="' + url + '"]'), $forms); - $matches.each(function (idx, el) { if ($(el).closest('#menu').length) { self.icinga.behaviors.navigation.resetActive(); @@ -367,7 +365,6 @@ }); } else { // TODO: next container url - // Get first container url? active = $('[href].active', req.$target).attr('href'); } From ec9e9e8dfbc9bddbdd5b68ef7f7ea38971603ca4 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 1 Oct 2014 16:49:30 +0200 Subject: [PATCH 23/62] Get correct navigation link id on first render --- public/js/icinga/behavior/navigation.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/js/icinga/behavior/navigation.js b/public/js/icinga/behavior/navigation.js index 67eaf69932..ab19886135 100644 --- a/public/js/icinga/behavior/navigation.js +++ b/public/js/icinga/behavior/navigation.js @@ -37,6 +37,9 @@ var $menus = $('[role="navigation"] li.active', el); if ($menus.size()) { activeMenuId = $menus[0].id; + $menus.find('li.active').first().each(function () { + activeMenuId = this.id; + }); } } }; From ee63dfd3101aa278f82a45b30d35a85c88021760 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 1 Oct 2014 17:47:21 +0200 Subject: [PATCH 24/62] Update navigation when the url of the main column changes --- public/js/icinga/behavior/navigation.js | 6 ++++++ public/js/icinga/loader.js | 8 ++++++-- public/js/icinga/ui.js | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/public/js/icinga/behavior/navigation.js b/public/js/icinga/behavior/navigation.js index ab19886135..615e5ac081 100644 --- a/public/js/icinga/behavior/navigation.js +++ b/public/js/icinga/behavior/navigation.js @@ -77,6 +77,12 @@ $menu.data('icinga-url', menuDataUrl); }; + Navigation.prototype.setActiveByUrl = function(url) + { + this.resetActive(); + this.setActive($('#menu [href="' + url + '"]')); + } + /** * Change the active menu element * diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index ae5b4b6457..9847df4e61 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -343,7 +343,9 @@ var $matches = $.merge($('[href="' + url + '"]'), $forms); $matches.each(function (idx, el) { if ($(el).closest('#menu').length) { - self.icinga.behaviors.navigation.resetActive(); + if (req.$target[0].id === 'col1') { + self.icinga.behaviors.navigation.resetActive(); + } } else if ($(el).closest('table.action').length) { $(el).closest('table.action').find('.active').removeClass('active'); } @@ -355,7 +357,9 @@ if ($el.is('form')) { $('input', $el).addClass('active'); } else { - self.icinga.behaviors.navigation.setActive($el); + if (req.$target[0].id === 'col1') { + self.icinga.behaviors.navigation.setActive($el); + } } // Interrupt .each, only on menu item shall be active return false; diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index 7f2507bdc7..ac98fb14ce 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -136,6 +136,7 @@ var kill = this.cutContainer($('#col1')); this.pasteContainer($('#col1'), col2); this.fixControls(); + this.icinga.behaviors.navigation.setActiveByUrl($('#col1').data('icingaUrl')); }, cutContainer: function ($col) { From 081d8eecfcad0f23da3eac56d3a3279029209d7c Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 2 Oct 2014 13:47:26 +0200 Subject: [PATCH 25/62] Implement abstract class EnumeratingFilterIterator --- .../Icinga/Util/EnumeratingFilterIterator.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 library/Icinga/Util/EnumeratingFilterIterator.php diff --git a/library/Icinga/Util/EnumeratingFilterIterator.php b/library/Icinga/Util/EnumeratingFilterIterator.php new file mode 100644 index 0000000000..44ef9b0c8c --- /dev/null +++ b/library/Icinga/Util/EnumeratingFilterIterator.php @@ -0,0 +1,37 @@ +index = 0; + } + + /** + * @return int + */ + public function key() + { + return $this->index++; + } +} From e192b939c26abe11639e0668c659e53d2846b9eb Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 2 Oct 2014 13:50:39 +0200 Subject: [PATCH 26/62] Icinga\Protocol\File\FileIterator: extend EnumeratingFilterIterator --- library/Icinga/Protocol/File/FileIterator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Protocol/File/FileIterator.php b/library/Icinga/Protocol/File/FileIterator.php index 2ec3c0ce52..72c4e0a391 100644 --- a/library/Icinga/Protocol/File/FileIterator.php +++ b/library/Icinga/Protocol/File/FileIterator.php @@ -4,7 +4,7 @@ namespace Icinga\Protocol\File; -use FilterIterator; +use Icinga\Util\EnumeratingFilterIterator; use Icinga\Util\File; /** @@ -12,7 +12,7 @@ * * Iterate over a file, yielding only fields of non-empty lines which match a PCRE expression */ -class FileIterator extends FilterIterator +class FileIterator extends EnumeratingFilterIterator { /** * A PCRE string with the fields to extract from the file's lines as named subpatterns From 051128b96741f9cd23282ba73b95b4657849272c Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 2 Oct 2014 13:55:52 +0200 Subject: [PATCH 27/62] Icinga\Protocol\File\FileReader: don't use Icinga\Util\Enumerate --- library/Icinga/Protocol/File/FileReader.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/Icinga/Protocol/File/FileReader.php b/library/Icinga/Protocol/File/FileReader.php index 2551489bf8..49da989ef2 100644 --- a/library/Icinga/Protocol/File/FileReader.php +++ b/library/Icinga/Protocol/File/FileReader.php @@ -6,7 +6,6 @@ use Icinga\Data\Selectable; use Countable; -use Icinga\Util\Enumerate; use Zend_Config; /** @@ -53,9 +52,7 @@ public function __construct(Zend_Config $config) */ public function iterate() { - return new Enumerate( - new FileIterator($this->filename, $this->fields) - ); + return new FileIterator($this->filename, $this->fields); } /** From 1a2d15b6387dfe0e2bf95d22e997654014a6cb39 Mon Sep 17 00:00:00 2001 From: Alexander Klimov Date: Thu, 2 Oct 2014 13:58:55 +0200 Subject: [PATCH 28/62] Drop Icinga\Util\Enumerate --- library/Icinga/Util/Enumerate.php | 62 ------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 library/Icinga/Util/Enumerate.php diff --git a/library/Icinga/Util/Enumerate.php b/library/Icinga/Util/Enumerate.php deleted file mode 100644 index 0861f7f657..0000000000 --- a/library/Icinga/Util/Enumerate.php +++ /dev/null @@ -1,62 +0,0 @@ -iterator = $iterator; - } - - public function rewind() - { - $this->iterator->rewind(); - $this->key = 0; - } - - public function next() - { - $this->iterator->next(); - ++$this->key; - } - - public function valid() - { - return $this->iterator->valid(); - } - - public function current() - { - return $this->iterator->current(); - } - - public function key() - { - return $this->key; - } -} From 8106fe4f79141168a5d07736ab09530c861695fc Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 10:19:05 +0200 Subject: [PATCH 29/62] form: Add `FormElement' as base class for our elements --- library/Icinga/Web/Form/FormElement.php | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 library/Icinga/Web/Form/FormElement.php diff --git a/library/Icinga/Web/Form/FormElement.php b/library/Icinga/Web/Form/FormElement.php new file mode 100644 index 0000000000..690be1acf0 --- /dev/null +++ b/library/Icinga/Web/Form/FormElement.php @@ -0,0 +1,62 @@ +_disableLoadDefaultDecorators === true; + } + + /** + * Load default decorators + * + * Icinga Web 2 loads its own default element decorators. For loading Zend's default element decorators set + * FormElement::$_disableLoadDefaultDecorators to false. + * + * @return this + * @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators. + */ + public function loadDefaultDecorators() + { + if ($this->loadDefaultDecoratorsIsDisabled()) { + return $this; + } + + if (! isset($this->_disableLoadDefaultDecorators)) { + $decorators = $this->getDecorators(); + if (empty($decorators)) { + // Load Icinga Web 2's default element decorators + $this->addDecorators(Form::$defaultElementDecorators); + } + } else { + // Load Zend's default decorators + parent::loadDefaultDecorators(); + } + return $this; + } +} From 9db76bf371247bd000506ca2cfbc116c4e57df0f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 10:19:36 +0200 Subject: [PATCH 30/62] CsrfCounterMeasure: Save calls to `setRequired()' and `setIgnore()' --- .../Web/Form/Element/CsrfCounterMeasure.php | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php b/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php index 722b1d3232..e9bc37eddd 100644 --- a/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php +++ b/library/Icinga/Web/Form/Element/CsrfCounterMeasure.php @@ -4,8 +4,8 @@ namespace Icinga\Web\Form\Element; -use Zend_Form_Element_Xhtml; use Icinga\Web\Session; +use Icinga\Web\Form\FormElement; use Icinga\Web\Form\InvalidCSRFTokenException; /** @@ -13,7 +13,7 @@ * * You must not set a value to successfully use this element, just give it a name and you're good to go. */ -class CsrfCounterMeasure extends Zend_Form_Element_Xhtml +class CsrfCounterMeasure extends FormElement { /** * Default form view helper to use for rendering @@ -22,14 +22,26 @@ class CsrfCounterMeasure extends Zend_Form_Element_Xhtml */ public $helper = 'formHidden'; + /** + * Counter measure element is required + * + * @var bool + */ + protected $_ignore = true; + + /** + * Ignore element when retrieving values at form level + * + * @var bool + */ + protected $_required = true; + /** * Initialize this form element */ public function init() { - $this->setRequired(true); // Not requiring this element would not make any sense - $this->setIgnore(true); // We do not want this element's value being retrieved by Form::getValues() - $this->setDecorators(array('ViewHelper')); + $this->addDecorator('ViewHelper'); $this->setValue($this->generateCsrfToken()); } From 9be52a91486dda1ec13c41ab7bc629e34e32729e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 10:20:26 +0200 Subject: [PATCH 31/62] DateTimePicker: Extend our FormElement class fixes #7322 --- .../Web/Form/Element/DateTimePicker.php | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/library/Icinga/Web/Form/Element/DateTimePicker.php b/library/Icinga/Web/Form/Element/DateTimePicker.php index 47f9b26e9d..728262827e 100644 --- a/library/Icinga/Web/Form/Element/DateTimePicker.php +++ b/library/Icinga/Web/Form/Element/DateTimePicker.php @@ -5,27 +5,15 @@ namespace Icinga\Web\Form\Element; use DateTime; -use Zend_Form_Element; +use Icinga\Web\Form; +use Icinga\Web\Form\FormElement; use Icinga\Web\Form\Validator\DateTimeValidator; /** * A date-and-time input control - * - * @method DateTime getValue() */ -class DateTimePicker extends Zend_Form_Element +class DateTimePicker extends FormElement { - /** - * Disable default decorators - * - * \Icinga\Web\Form sets default decorators for elements. - * - * @var bool - * - * @see \Icinga\Web\Form::__construct() For default element decorators. - */ - protected $_disableLoadDefaultDecorators = true; - /** * Form view helper to use for rendering * @@ -54,7 +42,7 @@ class DateTimePicker extends Zend_Form_Element /** * (non-PHPDoc) - * @see \Zend_Form_Element::init() For the method documentation. + * @see Zend_Form_Element::init() For the method documentation. */ public function init() { From a3409b166dfa98aefe0399759d8776ef4c636a1f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 10:21:02 +0200 Subject: [PATCH 32/62] Note: Extend our FormElement class --- library/Icinga/Web/Form/Element/Note.php | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/library/Icinga/Web/Form/Element/Note.php b/library/Icinga/Web/Form/Element/Note.php index 5dd489f0d6..78881ab449 100644 --- a/library/Icinga/Web/Form/Element/Note.php +++ b/library/Icinga/Web/Form/Element/Note.php @@ -5,23 +5,13 @@ namespace Icinga\Web\Form\Element; use Zend_Form_Element; +use Icinga\Web\Form; /** * A note */ class Note extends Zend_Form_Element { - /** - * Disable default decorators - * - * \Icinga\Web\Form sets default decorators for elements. - * - * @var bool - * - * @see \Icinga\Web\Form::__construct() For default element decorators. - */ - protected $_disableLoadDefaultDecorators = true; - /** * Form view helper to use for rendering * @@ -36,6 +26,15 @@ class Note extends Zend_Form_Element */ protected $_ignore = true; + /** + * (non-PHPDoc) + * @see Zend_Form_Element::init() For the method documentation. + */ + public function init() + { + $this->setDecorators(Form::$defaultElementDecorators); + } + /** * Validate element value (pseudo) * From fa96a1cbbefc4eaf0b80942957ed6d8d105ab4bc Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 10:21:17 +0200 Subject: [PATCH 33/62] Number: Extend our FormElement class --- library/Icinga/Web/Form/Element/Number.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/library/Icinga/Web/Form/Element/Number.php b/library/Icinga/Web/Form/Element/Number.php index 75c08165cf..8361425142 100644 --- a/library/Icinga/Web/Form/Element/Number.php +++ b/library/Icinga/Web/Form/Element/Number.php @@ -4,24 +4,13 @@ namespace Icinga\Web\Form\Element; -use Zend_Form_Element; +use Icinga\Web\Form\FormElement; /** * A number input control */ -class Number extends Zend_Form_Element +class Number extends FormElement { - /** - * Disable default decorators - * - * \Icinga\Web\Form sets default decorators for elements. - * - * @var bool - * - * @see \Icinga\Web\Form::__construct() For default element decorators. - */ - protected $_disableLoadDefaultDecorators = true; - /** * Form view helper to use for rendering * From 2956d9e3423ba4527058a6b1593efa5af4e367f9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 10:42:24 +0200 Subject: [PATCH 34/62] form: Correctly set our default element decorators --- library/Icinga/Web/Form.php | 46 +++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 2763e073ef..b32a030755 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -5,6 +5,7 @@ namespace Icinga\Web; use LogicException; +use Zend_Config; use Zend_Form; use Zend_View_Interface; use Icinga\Application\Icinga; @@ -81,6 +82,19 @@ class Form extends Zend_Form */ protected $uidElementName = 'formUID'; + /** + * Default element decorators + * + * @var array + */ + public static $defaultElementDecorators = array( + 'ViewHelper', + 'Errors', + array('Description', array('tag' => 'span', 'class' => 'description')), + 'Label', + array('HtmlTag', array('tag' => 'div')) + ); + /** * Create a new form * @@ -105,16 +119,6 @@ public function __construct($options = null) throw new LogicException('The option `onSuccess\' is not callable'); } - if (! isset($options['elementDecorators'])) { - $options['elementDecorators'] = array( - 'ViewHelper', - 'Errors', - array('Description', array('tag' => 'span', 'class' => 'description')), - 'Label', - array('HtmlTag', array('tag' => 'div')) - ); - } - parent::__construct($options); } @@ -417,23 +421,35 @@ public function addSubForm(Zend_Form $form, $name = null, $order = null) /** * Create a new element * - * Additionally, all structural form element decorators by Zend are replaced with our own ones. + * Icinga Web 2 loads its own default element decorators. For loading Zend's default element decorators set the + * `disableLoadDefaultDecorators' option to any other value than `true'. For loading custom element decorators use + * the 'decorators' option. * * @param string $type String element type * @param string $name The name of the element to add - * @param array $options The options for the element + * @param mixed $options The options for the element * * @return Zend_Form_Element * - * @see Zend_Form::createElement() + * @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators. */ public function createElement($type, $name, $options = null) { - if (is_array($options) && ! isset($options['disableLoadDefaultDecorators'])) { - $options['disableLoadDefaultDecorators'] = true; + if ($options !== null) { + if ($options instanceof Zend_Config) { + $options = $options->toArray(); + } + if (! isset($options['decorators']) + && ! array_key_exists('disabledLoadDefaultDecorators', $options) + ) { + $options['decorators'] = static::$defaultElementDecorators; + } + } else { + $options = array('decorators' => static::$defaultElementDecorators); } $el = parent::createElement($type, $name, $options); + if ($el && $el->getAttrib('autosubmit')) { $el->addDecorator(new NoScriptApply()); // Non-JS environments $class = $el->getAttrib('class'); From 36681bb55ac226bc871730df9882897ffe82cd24 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Mon, 6 Oct 2014 11:30:48 +0200 Subject: [PATCH 35/62] Add new QueryException --- library/Icinga/Exception/QueryException.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 library/Icinga/Exception/QueryException.php diff --git a/library/Icinga/Exception/QueryException.php b/library/Icinga/Exception/QueryException.php new file mode 100644 index 0000000000..beab3fe53e --- /dev/null +++ b/library/Icinga/Exception/QueryException.php @@ -0,0 +1,12 @@ + Date: Mon, 6 Oct 2014 11:32:15 +0200 Subject: [PATCH 36/62] Implement validation for filter and sort columns --- .../library/Monitoring/DataView/DataView.php | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index 2b91c1673c..661412c438 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -6,12 +6,14 @@ use Countable; use Icinga\Data\Filter\Filter; +use Icinga\Data\Filter\FilterMatch; use Icinga\Data\SimpleQuery; use Icinga\Data\Browsable; use Icinga\Data\PivotTable; use Icinga\Data\Sortable; use Icinga\Data\ConnectionInterface; use Icinga\Data\Filterable; +use Icinga\Exception\QueryException; use Icinga\Web\Request; use Icinga\Web\Url; use Icinga\Module\Monitoring\Backend; @@ -206,13 +208,13 @@ public function sort($column = null, $order = null) if ($sortRules !== null) { if ($column === null) { $sortColumns = reset($sortRules); - if (!isset($sortColumns['columns'])) { + if (! isset($sortColumns['columns'])) { $sortColumns['columns'] = array(key($sortRules)); } } else { if (isset($sortRules[$column])) { $sortColumns = $sortRules[$column]; - if (!isset($sortColumns['columns'])) { + if (! isset($sortColumns['columns'])) { $sortColumns['columns'] = array($column); } } else { @@ -227,6 +229,13 @@ public function sort($column = null, $order = null) $order = (strtoupper($order) === self::SORT_ASC) ? 'ASC' : 'DESC'; foreach ($sortColumns['columns'] as $column) { + if (! $this->isValidFilterTarget($column)) { + throw new QueryException( + t('The sort column "%s" is not allowed in "%s".'), + $column, + get_class($this) + ); + } $this->query->order($column, $order); } $this->isSorted = true; @@ -289,15 +298,44 @@ public function getMappedField($field) */ public function getQuery() { - if (! $this->isSorted) { $this->sort(); } + if (! $this->isSorted) { + $this->order(); + } return $this->query; } public function applyFilter(Filter $filter) { + $this->validateFilterColumns($filter); + return $this->addFilter($filter); } + /** + * Validates recursive the Filter columns against the isValidFilterTarget() method + * + * @param Filter $filter + * + * @throws \Icinga\Data\Filter\FilterException + */ + public function validateFilterColumns(Filter $filter) + { + if ($filter instanceof FilterMatch) { + if (! $this->isValidFilterTarget($filter->getColumn())) { + throw new QueryException( + t('The filter column "%s" is not allowed here.'), + $filter->getColumn() + ); + } + } + + if (method_exists($filter, 'filters')) { + foreach ($filter->filters() as $filter) { + $this->validateFilterColumns($filter); + } + } + } + public function clearFilter() { $this->query->clearFilter(); From 97d2a920db4e59fdaa9bbe4623ca0ab3015f80d9 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Mon, 6 Oct 2014 11:34:04 +0200 Subject: [PATCH 37/62] Implement GROUP BY clause functionality --- library/Icinga/Data/Db/DbQuery.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index a759da6b54..c50049ee72 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -59,6 +59,13 @@ class DbQuery extends SimpleQuery */ protected $count; + /** + * GROUP BY clauses + * + * @var string|array + */ + protected $group; + protected function init() { $this->db = $this->ds->getDbAdapter(); @@ -100,6 +107,10 @@ public function getSelectQuery() } } + if ($this->group) { + $select->group($this->group); + } + $select->columns($this->columns); $this->applyFilterSql($select); @@ -300,4 +311,17 @@ public function __toString() { return (string) $this->getSelectQuery(); } + + /** + * Add a GROUP BY clause + * + * @param string|array $group + * + * @return $this + */ + public function group($group) + { + $this->group = $group; + return $this; + } } From a0122763a676be970ce017421e3b1053e1838e3b Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Mon, 6 Oct 2014 11:37:33 +0200 Subject: [PATCH 38/62] Fix Queries: Summaries, Host, Service --- .../Monitoring/Backend/Ido/Query/GroupsummaryQuery.php | 4 +++- .../monitoring/library/Monitoring/DataView/HostStatus.php | 5 +++-- .../monitoring/library/Monitoring/DataView/ServiceStatus.php | 1 + modules/monitoring/library/Monitoring/Object/Service.php | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php index c1e12a1ee5..ec79ef63c0 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php @@ -44,7 +44,6 @@ protected function joinBaseTables() $columns = array( 'object_type', 'host_state', - 'host_name' ); // Prepend group column since we'll use columns index 0 later for grouping @@ -61,6 +60,9 @@ protected function joinBaseTables() 'in_downtime' => 'host_in_downtime' ) ); + if (in_array('servicegroup', $this->desiredColumns)) { + $hosts->group(array('sgo.name1', 'ho.object_id', 'state', 'acknowledged', 'in_downtime')); + } $services = $this->createSubQuery( 'Status', $columns + array( diff --git a/modules/monitoring/library/Monitoring/DataView/HostStatus.php b/modules/monitoring/library/Monitoring/DataView/HostStatus.php index 60f2242647..ff2ecaf913 100644 --- a/modules/monitoring/library/Monitoring/DataView/HostStatus.php +++ b/modules/monitoring/library/Monitoring/DataView/HostStatus.php @@ -64,7 +64,8 @@ public function getColumns() 'host_percent_state_change', 'host_modified_host_attributes', 'host_severity', - 'host_problem' + 'host_problem', + 'host_ipv4' ); } @@ -105,7 +106,7 @@ public function getSortRules() public function getFilterColumns() { - return array('hostgroup', 'service_problems'); + return array('hostgroup', 'service_problems', 'servicegroup'); } public function isValidFilterTarget($column) diff --git a/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php b/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php index b81b567642..45cde745ff 100644 --- a/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php +++ b/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php @@ -101,6 +101,7 @@ public function getColumns() 'service_flap_detection_enabled', 'service_flap_detection_enabled_changed', 'service_modified_service_attributes', + 'service_host_name' ); } diff --git a/modules/monitoring/library/Monitoring/Object/Service.php b/modules/monitoring/library/Monitoring/Object/Service.php index ac6957eab1..36a763101c 100644 --- a/modules/monitoring/library/Monitoring/Object/Service.php +++ b/modules/monitoring/library/Monitoring/Object/Service.php @@ -191,7 +191,8 @@ protected function getDataView() 'service_flap_detection_enabled_changed', 'service_modified_service_attributes', 'service_process_performance_data', - 'service_percent_state_change' + 'service_percent_state_change', + 'service_host_name' )) ->where('host_name', $this->host->getName()) ->where('service_description', $this->service); From 865ef76cb8485ba589c9f25b0bc14ce18893e141 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 13:19:25 +0200 Subject: [PATCH 39/62] filter: Fix whitespace sanitizing --- library/Icinga/Data/Filter/Filter.php | 2 -- library/Icinga/Data/Filter/FilterExpression.php | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Data/Filter/Filter.php b/library/Icinga/Data/Filter/Filter.php index 866d161b83..64607a4a0c 100644 --- a/library/Icinga/Data/Filter/Filter.php +++ b/library/Icinga/Data/Filter/Filter.php @@ -131,8 +131,6 @@ public static function where($col, $filter) public static function expression($col, $op, $expression) { - $col = trim($col); - $expression = is_array($expression) ? array_map('trim', $expression) : trim($expression); switch ($op) { case '=': return new FilterMatch($col, $op, $expression); case '<': return new FilterLessThan($col, $op, $expression); diff --git a/library/Icinga/Data/Filter/FilterExpression.php b/library/Icinga/Data/Filter/FilterExpression.php index c294141665..8378b5ae3f 100644 --- a/library/Icinga/Data/Filter/FilterExpression.php +++ b/library/Icinga/Data/Filter/FilterExpression.php @@ -12,6 +12,8 @@ class FilterExpression extends Filter public function __construct($column, $sign, $expression) { + $column = trim($column); + $expression = is_array($expression) ? array_map('trim', $expression) : trim($expression); $this->column = $column; $this->sign = $sign; $this->expression = $expression; From 421263af0076523ea003a8296b6e9695e676f263 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Mon, 6 Oct 2014 13:35:17 +0200 Subject: [PATCH 40/62] Make LDAP Groups optional refs #7343 --- .../Backend/LdapUserBackend.php | 4 +- library/Icinga/Authentication/UserBackend.php | 62 ++++--------------- 2 files changed, 15 insertions(+), 51 deletions(-) diff --git a/library/Icinga/Authentication/Backend/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php index 247f638537..8ef6c89de9 100644 --- a/library/Icinga/Authentication/Backend/LdapUserBackend.php +++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php @@ -90,13 +90,15 @@ public function assertAuthenticationPossible() /** * Retrieve the user groups * + * @TODO: Subject to change, see #7343 + * * @param string $dn * * @return array|null */ public function getGroups($dn) { - if (empty($this->groupOptions)) { + if (empty($this->groupOptions) || ! isset($this->groupOptions['group_base_dn'])) { return null; } diff --git a/library/Icinga/Authentication/UserBackend.php b/library/Icinga/Authentication/UserBackend.php index b475e9135d..7829210fd8 100644 --- a/library/Icinga/Authentication/UserBackend.php +++ b/library/Icinga/Authentication/UserBackend.php @@ -93,7 +93,6 @@ public static function create($name, Zend_Config $backendConfig) $backend = new DbUserBackend($resource); break; case 'msldap': - self::checkLdapConfiguration($name, $backendConfig); $groupOptions = array( 'group_base_dn' => $backendConfig->group_base_dn, 'group_attribute' => $backendConfig->group_attribute, @@ -108,7 +107,18 @@ public static function create($name, Zend_Config $backendConfig) ); break; case 'ldap': - self::checkLdapConfiguration($name, $backendConfig); + if ($backendConfig->user_class === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the user_class directive', + $name + ); + } + if ($backendConfig->user_name_attribute === null) { + throw new ConfigurationError( + 'Authentication configuration for backend "%s" is missing the user_name_attribute directive', + $name + ); + } $groupOptions = array( 'group_base_dn' => $backendConfig->group_base_dn, 'group_attribute' => $backendConfig->group_attribute, @@ -152,52 +162,4 @@ abstract public function hasUser(User $user); * @return bool */ abstract public function authenticate(User $user, $password); - - /** - * Checks the ldap configuration - * - * @param $name - * @param Zend_Config $backendConfig - * - * @throws \Icinga\Exception\ConfigurationError - */ - protected static function checkLdapConfiguration($name, Zend_Config $backendConfig) - { - if ($backendConfig->user_class === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the user_class directive', - $name - ); - } - if ($backendConfig->user_name_attribute === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the user_name_attribute directive', - $name - ); - } - if ($backendConfig->group_base_dn === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the group_base_dn directive', - $name - ); - } - if ($backendConfig->group_attribute === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the group_attribute directive', - $name - ); - } - if ($backendConfig->group_member_attribute === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the group_member_attribute directive', - $name - ); - } - if ($backendConfig->group_class === null) { - throw new ConfigurationError( - 'Authentication configuration for backend "%s" is missing the group_class directive', - $name - ); - } - } } From 8a8ce49f7315317114a5557b11ea91a7503fd82f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Oct 2014 17:07:37 +0200 Subject: [PATCH 41/62] monitoring: Call `hasBetterUrl()' in every list action fixes #6679 --- .../controllers/ListController.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index d3427e6a49..9648145b7e 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -265,6 +265,9 @@ public function servicesAction() */ public function downtimesAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('downtimes'); $this->setAutorefreshInterval(12); $query = $this->backend->select()->from('downtime', array( @@ -309,6 +312,9 @@ public function downtimesAction() */ public function notificationsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('notifications'); $this->setAutorefreshInterval(15); $query = $this->backend->select()->from('notification', array( @@ -328,6 +334,9 @@ public function notificationsAction() public function contactsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('contacts'); $query = $this->backend->select()->from('contact', array( 'contact_name', @@ -364,6 +373,9 @@ public function contactsAction() public function statehistorysummaryAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('statehistorysummary', 'State Summary'); $form = new StatehistoryForm(); @@ -401,6 +413,9 @@ public function statehistorysummaryAction() public function contactgroupsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('contactgroups'); $query = $this->backend->select()->from('contactgroup', array( 'contactgroup_name', @@ -430,6 +445,9 @@ public function contactgroupsAction() public function commentsAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('comments'); $this->setAutorefreshInterval(12); $query = $this->backend->select()->from('comment', array( @@ -550,6 +568,9 @@ public function eventhistoryAction() public function servicematrixAction() { + if ($url = $this->hasBetterUrl()) { + return $this->redirectNow($url); + } $this->addTitleTab('servicematrix'); $this->setAutorefreshInterval(15); $query = $this->backend->select()->from('serviceStatus', array( From f954e8ce48e2cb7cd9bc33277338f972cf7d573b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 7 Oct 2014 11:47:24 +0200 Subject: [PATCH 42/62] monitoring: Remove obsolete helper `ResolveComments' --- .../views/helpers/ResolveComments.php | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 modules/monitoring/application/views/helpers/ResolveComments.php diff --git a/modules/monitoring/application/views/helpers/ResolveComments.php b/modules/monitoring/application/views/helpers/ResolveComments.php deleted file mode 100644 index 6f62ee1d23..0000000000 --- a/modules/monitoring/application/views/helpers/ResolveComments.php +++ /dev/null @@ -1,33 +0,0 @@ - Date: Tue, 7 Oct 2014 13:21:28 +0200 Subject: [PATCH 43/62] monitoring: Fix link to an object's contact in the detail views --- .../scripts/show/components/contacts.phtml | 4 +- .../Backend/Ido/Query/ContactgroupQuery.php | 54 +++++++++---------- .../library/Monitoring/DataView/Contact.php | 1 + .../Monitoring/DataView/Contactgroup.php | 2 + 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/modules/monitoring/application/views/scripts/show/components/contacts.phtml b/modules/monitoring/application/views/scripts/show/components/contacts.phtml index 550c87e129..5cb0a1d515 100644 --- a/modules/monitoring/application/views/scripts/show/components/contacts.phtml +++ b/modules/monitoring/application/views/scripts/show/components/contacts.phtml @@ -5,7 +5,7 @@ if (! empty($object->contacts)) { $list = array(); foreach ($object->contacts as $contact) { $list[] = $this->qlink($contact->contact_alias, 'monitoring/show/contact', array( - 'contact_name' => $contact->contact_name + 'contact' => $contact->contact_name )); } @@ -24,7 +24,7 @@ if (! empty($object->contactgroups)) { $list[] = $this->qlink( $contactgroup->contactgroup_alias, 'monitoring/list/contactgroups', - array('contactgroup_name' => $contactgroup->contactgroup_name) + array('contactgroup' => $contactgroup->contactgroup_name) ); } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php index aef50ebdb1..346f2458a2 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php @@ -8,39 +8,39 @@ class ContactgroupQuery extends IdoQuery { protected $columnMap = array( 'contactgroups' => array( - 'contactgroup_name' => 'cgo.name1 COLLATE latin1_general_ci', - 'contactgroup_alias' => 'cg.alias', + 'contactgroup' => 'cgo.name1 COLLATE latin1_general_ci', + 'contactgroup_name' => 'cgo.name1 COLLATE latin1_general_ci', + 'contactgroup_alias' => 'cg.alias', ), 'contacts' => array( - 'contact_name' => 'co.name1 COLLATE latin1_general_ci', - 'contact_alias' => 'c.alias', - 'contact_email' => 'c.email_address', - 'contact_pager' => 'c.pager_address', - 'contact_has_host_notfications' => 'c.host_notifications_enabled', - 'contact_has_service_notfications' => 'c.service_notifications_enabled', - 'contact_can_submit_commands' => 'c.can_submit_commands', - 'contact_notify_service_recovery' => 'c.notify_service_recovery', - 'contact_notify_service_warning' => 'c.notify_service_warning', - 'contact_notify_service_critical' => 'c.notify_service_critical', - 'contact_notify_service_unknown' => 'c.notify_service_unknown', - 'contact_notify_service_flapping' => 'c.notify_service_flapping', - 'contact_notify_service_downtime' => 'c.notify_service_recovery', - 'contact_notify_host_recovery' => 'c.notify_host_recovery', - 'contact_notify_host_down' => 'c.notify_host_down', - 'contact_notify_host_unreachable' => 'c.notify_host_unreachable', - 'contact_notify_host_flapping' => 'c.notify_host_flapping', - 'contact_notify_host_downtime' => 'c.notify_host_downtime', + 'contact' => 'co.name1 COLLATE latin1_general_ci', + 'contact_name' => 'co.name1 COLLATE latin1_general_ci', + 'contact_alias' => 'c.alias', + 'contact_email' => 'c.email_address', + 'contact_pager' => 'c.pager_address', + 'contact_has_host_notfications' => 'c.host_notifications_enabled', + 'contact_has_service_notfications' => 'c.service_notifications_enabled', + 'contact_can_submit_commands' => 'c.can_submit_commands', + 'contact_notify_service_recovery' => 'c.notify_service_recovery', + 'contact_notify_service_warning' => 'c.notify_service_warning', + 'contact_notify_service_critical' => 'c.notify_service_critical', + 'contact_notify_service_unknown' => 'c.notify_service_unknown', + 'contact_notify_service_flapping' => 'c.notify_service_flapping', + 'contact_notify_service_downtime' => 'c.notify_service_recovery', + 'contact_notify_host_recovery' => 'c.notify_host_recovery', + 'contact_notify_host_down' => 'c.notify_host_down', + 'contact_notify_host_unreachable' => 'c.notify_host_unreachable', + 'contact_notify_host_flapping' => 'c.notify_host_flapping', + 'contact_notify_host_downtime' => 'c.notify_host_downtime', ), 'hosts' => array( - 'host_object_id' => 'ho.object_id', - 'host_name' => 'ho.name1', - 'host' => 'ho.name1' + 'host' => 'ho.name1', + 'host_name' => 'ho.name1' ), 'services' => array( - 'service_object_id' => 'so.object_id', - 'service_host_name' => 'so.name1 COLLATE latin1_general_ci', - 'service' => 'so.name2 COLLATE latin1_general_ci', - 'service_description' => 'so.name2 COLLATE latin1_general_ci', + 'service' => 'so.name2 COLLATE latin1_general_ci', + 'service_description' => 'so.name2 COLLATE latin1_general_ci', + 'service_host_name' => 'so.name1 COLLATE latin1_general_ci' ) ); diff --git a/modules/monitoring/library/Monitoring/DataView/Contact.php b/modules/monitoring/library/Monitoring/DataView/Contact.php index 9f78a3b6ad..27639b0c63 100644 --- a/modules/monitoring/library/Monitoring/DataView/Contact.php +++ b/modules/monitoring/library/Monitoring/DataView/Contact.php @@ -18,6 +18,7 @@ class Contact extends DataView public function getColumns() { return array( + 'contact', 'contact_name', 'contact_alias', 'contact_email', diff --git a/modules/monitoring/library/Monitoring/DataView/Contactgroup.php b/modules/monitoring/library/Monitoring/DataView/Contactgroup.php index 870a5407b6..7dd7a1cd41 100644 --- a/modules/monitoring/library/Monitoring/DataView/Contactgroup.php +++ b/modules/monitoring/library/Monitoring/DataView/Contactgroup.php @@ -18,7 +18,9 @@ class Contactgroup extends DataView public function getColumns() { return array( + 'contact', 'contact_name', + 'contactgroup', 'contactgroup_name', 'contactgroup_alias', 'host', From 1b23fd7fbeb058d3d466d500b398a689a390ecde Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 7 Oct 2014 13:22:07 +0200 Subject: [PATCH 44/62] monitoring: Don't support status.dat as backend Icinga Web 2 will not support status.dat for now and maybe forever. --- .../Config/Resource/StatusdatResourceForm.php | 54 -- .../forms/Config/ResourceConfigForm.php | 4 - library/Icinga/Data/ResourceFactory.php | 8 +- .../Statusdat/Exception/ParsingException.php | 13 - library/Icinga/Protocol/Statusdat/IReader.php | 20 - .../Protocol/Statusdat/ObjectContainer.php | 51 -- library/Icinga/Protocol/Statusdat/Parser.php | 422 ---------------- .../Protocol/Statusdat/PrintableObject.php | 20 - library/Icinga/Protocol/Statusdat/Query.php | 460 ------------------ .../Protocol/Statusdat/Query/Expression.php | 415 ---------------- .../Icinga/Protocol/Statusdat/Query/Group.php | 397 --------------- .../Protocol/Statusdat/Query/IQueryPart.php | 36 -- library/Icinga/Protocol/Statusdat/Reader.php | 330 ------------- .../Statusdat/RuntimeStateContainer.php | 72 --- .../Statusdat/View/AccessorStrategy.php | 42 -- .../Statusdat/View/MonitoringObjectList.php | 143 ------ .../forms/Config/BackendConfigForm.php | 7 +- .../Backend/Statusdat/Query/CommentQuery.php | 83 ---- .../Backend/Statusdat/Query/ContactQuery.php | 133 ----- .../Statusdat/Query/ContactgroupQuery.php | 61 --- .../Backend/Statusdat/Query/DowntimeQuery.php | 79 --- .../Statusdat/Query/GroupsummaryQuery.php | 248 ---------- .../Statusdat/Query/HostgroupQuery.php | 25 - .../Backend/Statusdat/Query/HostlistQuery.php | 33 -- .../Statusdat/Query/ServicegroupQuery.php | 28 -- .../Query/ServicegroupsummaryQuery.php | 22 - .../Statusdat/Query/ServicelistQuery.php | 30 -- .../Backend/Statusdat/Query/StatusQuery.php | 361 -------------- .../Statusdat/Query/StatusSummaryQuery.php | 115 ----- .../Statusdat/Query/StatusdatQuery.php | 218 --------- 30 files changed, 4 insertions(+), 3926 deletions(-) delete mode 100644 application/forms/Config/Resource/StatusdatResourceForm.php delete mode 100644 library/Icinga/Protocol/Statusdat/Exception/ParsingException.php delete mode 100644 library/Icinga/Protocol/Statusdat/IReader.php delete mode 100644 library/Icinga/Protocol/Statusdat/ObjectContainer.php delete mode 100644 library/Icinga/Protocol/Statusdat/Parser.php delete mode 100644 library/Icinga/Protocol/Statusdat/PrintableObject.php delete mode 100644 library/Icinga/Protocol/Statusdat/Query.php delete mode 100644 library/Icinga/Protocol/Statusdat/Query/Expression.php delete mode 100644 library/Icinga/Protocol/Statusdat/Query/Group.php delete mode 100644 library/Icinga/Protocol/Statusdat/Query/IQueryPart.php delete mode 100644 library/Icinga/Protocol/Statusdat/Reader.php delete mode 100644 library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php delete mode 100644 library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php delete mode 100644 library/Icinga/Protocol/Statusdat/View/MonitoringObjectList.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicelistQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php delete mode 100644 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php diff --git a/application/forms/Config/Resource/StatusdatResourceForm.php b/application/forms/Config/Resource/StatusdatResourceForm.php deleted file mode 100644 index f150056d10..0000000000 --- a/application/forms/Config/Resource/StatusdatResourceForm.php +++ /dev/null @@ -1,54 +0,0 @@ -setName('form_config_resource_statusdat'); - } - - /** - * @see Form::createElements() - */ - public function createElements(array $formData) - { - $this->addElement( - 'text', - 'status_file', - array( - 'required' => true, - 'label' => t('Filepath'), - 'description' => t('Location of your icinga status.dat file'), - 'value' => realpath(Icinga::app()->getApplicationDir() . '/../var/status.dat'), - 'validators' => array(new ReadablePathValidator()) - ) - ); - $this->addElement( - 'text', - 'object_file', - array( - 'required' => true, - 'label' => t('Filepath'), - 'description' => t('Location of your icinga objects.cache file'), - 'value' => realpath(Icinga::app()->getApplicationDir() . '/../var/objects.cache'), - 'validators' => array(new ReadablePathValidator()) - ) - ); - - return $this; - } -} diff --git a/application/forms/Config/ResourceConfigForm.php b/application/forms/Config/ResourceConfigForm.php index 8e677cf8b2..1aa7edc966 100644 --- a/application/forms/Config/ResourceConfigForm.php +++ b/application/forms/Config/ResourceConfigForm.php @@ -12,7 +12,6 @@ use Icinga\Form\Config\Resource\FileResourceForm; use Icinga\Form\Config\Resource\LdapResourceForm; use Icinga\Form\Config\Resource\LivestatusResourceForm; -use Icinga\Form\Config\Resource\StatusdatResourceForm; use Icinga\Application\Platform; use Icinga\Exception\ConfigurationError; @@ -42,8 +41,6 @@ public function getResourceForm($type) return new LdapResourceForm(); } elseif ($type === 'livestatus') { return new LivestatusResourceForm(); - } elseif ($type === 'statusdat') { - return new StatusdatResourceForm(); } elseif ($type === 'file') { return new FileResourceForm(); } else { @@ -214,7 +211,6 @@ public function createElements(array $formData) $resourceTypes = array( 'file' => t('File'), - 'statusdat' => 'Status.dat', 'livestatus' => 'Livestatus', ); if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) { diff --git a/library/Icinga/Data/ResourceFactory.php b/library/Icinga/Data/ResourceFactory.php index 30597ad2d6..e85413615c 100644 --- a/library/Icinga/Data/ResourceFactory.php +++ b/library/Icinga/Data/ResourceFactory.php @@ -10,7 +10,6 @@ use Icinga\Exception\ConfigurationError; use Icinga\Data\Db\DbConnection; use Icinga\Protocol\Livestatus\Connection as LivestatusConnection; -use Icinga\Protocol\Statusdat\Reader as StatusdatReader; use Icinga\Protocol\Ldap\Connection as LdapConnection; use Icinga\Protocol\File\FileReader; @@ -102,7 +101,7 @@ private static function assertResourcesExist() * * @param Zend_Config $config The configuration for the created resource. * - * @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader An objects that can be used to access + * @return DbConnection|LdapConnection|LivestatusConnection An object that can be used to access * the given resource. The returned class depends on the configuration property 'type'. * @throws ConfigurationError When an unsupported type is given */ @@ -115,9 +114,6 @@ public static function createResource(Zend_Config $config) case 'ldap': $resource = new LdapConnection($config); break; - case 'statusdat': - $resource = new StatusdatReader($config); - break; case 'livestatus': $resource = new LivestatusConnection($config->socket); break; @@ -137,7 +133,7 @@ public static function createResource(Zend_Config $config) * Create a resource from name * * @param string $resourceName - * @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader + * @return DbConnection|LdapConnection|LivestatusConnection */ public static function create($resourceName) { diff --git a/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php b/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php deleted file mode 100644 index 0947e65bc2..0000000000 --- a/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php +++ /dev/null @@ -1,13 +0,0 @@ -ref = & $obj; - $this->reader = & $reader; - } - - /** - * @param $attribute - * @return \stdClass - */ - public function __get($attribute) - { - $exploded = explode(".", $attribute); - $result = $this->ref; - - foreach ($exploded as $elem) { - if (isset($result->$elem)) { - $result = $result->$elem; - } else { - return null; - } - } - return $result; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Parser.php b/library/Icinga/Protocol/Statusdat/Parser.php deleted file mode 100644 index 5b36945182..0000000000 --- a/library/Icinga/Protocol/Statusdat/Parser.php +++ /dev/null @@ -1,422 +0,0 @@ -file = $file; - $this->icingaState = $baseState; - } - - /** - * Parse the given file handle as an objects file and read object information - */ - public function parseObjectsFile() - { - $DEFINE = strlen('define '); - $this->icingaState = array(); - foreach ($this->file as $line) { - $line = trim($line); - $this->lineCtr++; - if ($line === '' || $line[0] === '#') { - continue; - } - $this->currentObjectType = trim(substr($line, $DEFINE, -1)); - if (!isset($this->icingaState[$this->currentObjectType])) { - $this->icingaState[$this->currentObjectType] = array(); - } - $this->readCurrentObject(); - } - $this->processDeferred(); - } - - /** - * Parse the given file as an status.dat file and read runtime information - * - * @param File $file The file to parse or null to parse the one passed to the constructor - */ - public function parseRuntimeState(File $file = null) - { - if ($file != null) { - $this->file = $file; - } else { - $file = $this->file; - } - - if (!$this->icingaState) { - throw new ProgrammingError('Tried to read runtime state without existing objects data'); - } - $this->overwrites = array(); - foreach ($file as $line) { - $line = trim($line); - $this->lineCtr++; - if ($line === '' || $line[0] === '#') { - continue; - } - $this->currentStateType = trim(substr($line, 0, -1)); - $this->readCurrentState(); - } - } - - /** - * Read the next object from the object.cache file handle - * - * @throws ParsingException - */ - private function readCurrentObject() - { - $monitoringObject = new PrintableObject(); - foreach ($this->file as $line) { - $line = explode("\t", trim($line), 2); - $this->lineCtr++; - if (!$line) { - continue; - } - - // End of object - if ($line[0] === '}') { - $this->registerObject($monitoringObject); - return; - } - if (!isset($line[1])) { - $line[1] = ''; - } - $monitoringObject->{$line[0]} = trim($line[1]); - } - throw new ParsingException('Unexpected EOF in objects.cache, line ' . $this->lineCtr); - } - - /** - * Read the next state from the status.dat file handler - * - * @throws Exception\ParsingException - */ - private function readCurrentState() - { - $statusdatObject = new RuntimeStateContainer(); - - $objectType = $this->getObjectTypeForState(); - - if ($objectType != 'host' && $objectType != 'service') { - $this->skipObject(); // ignore unknown objects - return; - } - if (!isset($this->icingaState[$this->currentObjectType])) { - throw new ParsingException("No $this->currentObjectType objects registered in objects.cache"); - } - $base = & $this->icingaState[$this->currentObjectType]; - $state = $this->skipObject(true); - $statusdatObject->runtimeState = & $state; - $name = $this->getObjectIdentifier($statusdatObject); - - if (!isset($base[$name])) { - throw new ParsingException( - "Unknown object $name " . $this->currentObjectType . ' - ' - . print_r( - $statusdatObject, - true - ) - . "\n" . print_r($base, true) - ); - } - $type = substr($this->currentStateType, strlen($objectType)); - - if ($type == 'status') { - // directly set the status to the status field of the given object - $base[$name]->status = & $statusdatObject; - } else { - if (!isset($base[$name]->$type) || !in_array($base[$name]->$type, $this->overwrites)) { - $base[$name]->$type = array(); - $this->overwrites[] = & $base[$name]->$type; - } - array_push($base[$name]->$type, $statusdatObject); - $this->currentObjectType = $type; - if (!isset($this->icingaState[$type])) { - $this->icingaState[$type] = array(); - } - $this->icingaState[$type][] = &$statusdatObject; - $id = $this->getObjectIdentifier($statusdatObject); - if ($id !== false && isset($this->icingaState[$objectType][$id])) { - $statusdatObject->$objectType = $this->icingaState[$objectType][$id]; - } - } - - return; - - } - - /** - * Get the corresponding object type name for the given state - * - * @return string - */ - private function getObjectTypeForState() - { - $pos = strpos($this->currentStateType, 'service'); - - if ($pos === false) { - $pos = strpos($this->currentStateType, 'host'); - } else { - $this->currentObjectType = 'service'; - return 'service'; - } - - if ($pos === false) { - return $this->currentStateType; - } else { - $this->currentObjectType = 'host'; - return 'host'; - } - - return $this->currentObjectType; - } - - /** - * Skip the current object definition - * - * @param bool $returnString If true, the object string will be returned - * @return string The skipped object if $returnString is true - */ - protected function skipObject($returnString = false) - { - if (!$returnString) { - while (trim($this->file->fgets()) !== '}') { - } - return null; - } else { - $str = ''; - while (($val = trim($this->file->fgets())) !== '}') { - $str .= $val . "\n"; - } - return $str; - } - } - - /** - * Register the given object in the icinga state - * - * @param object $object The monitoring object to register - */ - protected function registerObject(&$object) - { - - $name = $this->getObjectIdentifier($object); - if ($name !== false) { - $this->icingaState[$this->currentObjectType][$name] = &$object; - } - $this->registerObjectAsProperty($object); - } - - /** - * Register the given object as a property in related objects - * - * This registers for example hosts underneath their hostgroup and vice cersa - * - * @param object $object The object to register as a property - */ - protected function registerObjectAsProperty(&$object) - { - if ($this->currentObjectType == 'service' - || $this->currentObjectType == 'host' - || $this->currentObjectType == 'contact') { - return null; - } - $isService = strpos($this->currentObjectType, 'service') !== false; - $isHost = strpos($this->currentObjectType, 'host') !== false; - $isContact = strpos($this->currentObjectType, 'contact') !== false; - $name = $this->getObjectIdentifier($object); - - if ($isService === false && $isHost === false && $isContact === false) { - // this would be error in the parser implementation - return null; - } - $property = $this->currentObjectType; - if ($isService) { - $this->currentObjectType = 'service'; - $property = substr($property, strlen('service')); - } elseif ($isHost) { - $this->currentObjectType = 'host'; - $property = substr($property, strlen('host')); - } elseif ($isContact) { - $this->currentObjectType = 'contact'; - $property = substr($property, strlen('contact')); - } - - if (!isset($this->icingaState[$this->currentObjectType])) { - return $this->deferRegistration($object, $this->currentObjectType . $property); - } - - // @TODO: Clean up, this differates between 1:n and 1:1 references - if (strpos($property, 'group') !== false) { - $sourceIdentifier = $this->getMembers($object); - foreach ($sourceIdentifier as $id) { - $source = $this->icingaState[$this->currentObjectType][$id]; - if (!isset($source->$property)) { - $source->$property = array(); - } - $type = $this->currentObjectType; - if (!isset($object->$type)) { - $object->$type = array(); - } - // add the member to the group object - array_push($object->$type, $source); - // add the group to the member object - array_push($source->$property, $name); - } - } else { - $source = $this->icingaState[$this->currentObjectType][$this->getObjectIdentifier($object)]; - if (!isset($source->$property)) { - $source->$property = array(); - } - - array_push($source->$property, $object); - } - - return null; - } - - /** - * Defer registration of the given object - * - * @param object $object The object to defer - * @param String $objType The name of the object type - */ - protected function deferRegistration($object, $objType) - { - $this->deferred[] = array($object, $objType); - } - - /** - * Process deferred objects - */ - protected function processDeferred() - { - foreach ($this->deferred as $obj) { - $this->currentObjectType = $obj[1]; - $this->registerObjectAsProperty($obj[0]); - } - } - - /** - * Return the resolved members directive of an object - * - * @param object $object The object to get the members from - * @return array An array of member names - */ - protected function getMembers(&$object) - { - if (!isset($object->members)) { - return array(); - } - - $members = explode(',', $object->members); - - if ($this->currentObjectType == 'service') { - $res = array(); - for ($i = 0; $i < count($members); $i += 2) { - $res[] = $members[$i] . ';' . $members[$i + 1]; - } - return $res; - } else { - return $members; - } - - } - - /** - * Return the unique name of the given object - * - * @param object $object The object to retrieve the name from - * @return string The name of the object or null if no name can be retrieved - */ - protected function getObjectIdentifier(&$object) - { - if ($this->currentObjectType == 'contact') { - return $object->contact_name; - } - - if ($this->currentObjectType == 'service') { - return $object->host_name . ';' . $object->service_description; - } - $name = $this->currentObjectType . '_name'; - if (isset($object->{$name})) { - return $object->{$name}; - } - if (isset($object->service_description)) { - return $object->host_name . ';' . $object->service_description; - } elseif (isset($object->host_name)) { - return $object->host_name; - } - return null; - - } - - /** - * Return the internal state of the parser - * - * @return null - */ - public function getRuntimeState() - { - return $this->icingaState; - } -} diff --git a/library/Icinga/Protocol/Statusdat/PrintableObject.php b/library/Icinga/Protocol/Statusdat/PrintableObject.php deleted file mode 100644 index 840a7aa2af..0000000000 --- a/library/Icinga/Protocol/Statusdat/PrintableObject.php +++ /dev/null @@ -1,20 +0,0 @@ -contact_name)) { - return $this->contact_name; - } elseif (isset($this->service_description)) { - return $this->service_description; - } elseif (isset($this->host_name)) { - return $this->host_name; - } - return ''; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query.php b/library/Icinga/Protocol/Statusdat/Query.php deleted file mode 100644 index f3b5ea85e5..0000000000 --- a/library/Icinga/Protocol/Statusdat/Query.php +++ /dev/null @@ -1,460 +0,0 @@ - array('host'), - 'services' => array('service'), - 'downtimes' => array('downtime'), - 'groups' => array('hostgroup', 'servicegroup'), - 'hostgroups' => array('hostgroup'), - 'servicegroups' => array('servicegroup'), - 'comments' => array('comment'), - 'contacts' => array('contact'), - 'contactgroups' => array('contactgroup') - ); - - /** - * The current StatusDat query that will be applied upon calling fetchAll - * - * @var IQueryPart - */ - private $queryFilter = null; - - /** - * The current query source being used - * - * @var string - */ - private $source = ''; - - /** - * An array containing all columns used for sorting - * - * @var array - */ - protected $orderColumns = array(); - - /** - * An array containig all columns used for (simple) grouping - * - * @var array - */ - private $groupColumns = array(); - - /** - * An optional function callback to use for more specific grouping - * - * @var array - */ - private $groupByFn = null; - - /** - * The scope index for the callback function - */ - const FN_SCOPE = 0; - - /** - * The name index for the callback function - */ - const FN_NAME = 1; - - /** - * Return true if columns are set for this query - * - * @return bool - */ - public function hasColumns() - { - $columns = $this->getColumns(); - return !empty($columns); - } - - /** - * Set the status.dat specific IQueryPart filter to use - * - * @param IQueryPart $filter - */ - public function setQueryFilter($filter) - { - $this->queryFilter = $filter; - } - - /** - * Order the query result by the given columns - * - * @param String|array $columns An array of columns to order by - * @param String $dir The direction (asc or desc) in string form - * - * @return $this Fluent interface - */ - public function order($columns, $dir = null, $isFunction = false) - { - if ($dir && strtolower($dir) == 'desc') { - $dir = self::SORT_DESC; - } else { - $dir = self::SORT_ASC; - } - if (!is_array($columns)) { - $columns = array($columns); - } - - foreach ($columns as $col) { - if (($pos = strpos($col, ' ')) !== false) { - $dir = strtoupper(substr($col, $pos + 1)); - if ($dir === 'DESC') { - $dir = self::SORT_DESC; - } else { - $dir = self::SORT_ASC; - } - $col = substr($col, 0, $pos); - } else { - $col = $col; - } - - $this->orderColumns[] = array($col, $dir); - } - return $this; - } - - /** - * Order the query result using the callback to retrieve values for items - * - * @param array $columns A scope, function array to use for retrieving the values when ordering - * @param String $dir The direction (asc or desc) in string form - * - * @return $this Fluent interface - */ - public function orderByFn(array $callBack, $dir = null) - { - if ($dir && strtolower($dir) == 'desc') { - $dir = self::SORT_DESC; - } else { - $dir = self::SORT_ASC; - } - $this->orderColumns[] = array($callBack, $dir); - } - - - - /** - * Set the query target - * - * @param String $table The table/target to select the query from - * @param array $columns An array of attributes to use (required for fetchPairs()) - * - * @return $this Fluent interface - * @throws IcingaException If the target is unknonw - */ - public function from($table, array $attributes = null) - { - if (!$this->getColumns() && $attributes) { - $this->setColumns($attributes); - } - if (isset(self::$VALID_TARGETS[$table])) { - $this->source = $table; - } else { - throw new IcingaException( - 'Unknown from target for status.dat :%s', - $table - ); - } - return $this; - } - - /** - * Return an index of all objects matching the filter of this query - * - * This index will be used for ordering, grouping and limiting - */ - private function getFilteredIndices($classType = '\Icinga\Protocol\Statusdat\Query\Group') - { - $baseGroup = $this->queryFilter; - $state = $this->ds->getState(); - $result = array(); - $source = self::$VALID_TARGETS[$this->source]; - - foreach ($source as $target) { - - if (! isset($state[$target])) { - continue; - } - - $indexes = array_keys($state[$target]); - if ($baseGroup) { - $baseGroup->setQuery($this); - $idx = array_keys($state[$target]); - $indexes = $baseGroup->filter($state[$target], $idx); - } - if (!isset($result[$target])) { - $result[$target] = $indexes; - } else { - array_merge($result[$target], $indexes); - } - } - return $result; - } - - /** - * Order the given result set - * - * @param array $indices The result set of the query that should be ordered - */ - private function orderIndices(array &$indices) - { - if (!empty($this->orderColumns)) { - foreach ($indices as $type => &$subindices) { - $this->currentType = $type; - usort($subindices, array($this, 'orderResult')); - } - } - } - - /** - * Start a query - * - * This is just a dummy function to allow a more convenient syntax - * - * @return self Fluent interface - */ - public function select() - { - return $this; - } - - /** - * Order implementation called by usort - * - * @param String $a The left object index - * @param Strinv $b The right object index - * @return int 0, 1 or -1, see usort for detail - */ - private function orderResult($a, $b) - { - $o1 = $this->ds->getObjectByName($this->currentType, $a); - $o2 = $this->ds->getObjectByName($this->currentType, $b); - $result = 0; - - foreach ($this->orderColumns as &$col) { - if (is_array($col[0])) { - // sort by function - $result += $col[1] * strnatcasecmp( - $col[0][0]->$col[0][1]($o1), - $col[0][0]->$col[0][1]($o2) - ); - } else { - $result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]}); - } - } - return $result; - } - - /** - * Limit the given resultset - * - * @param array $indices The filtered, ordered indices - */ - private function limitIndices(array &$indices) - { - foreach ($indices as $type => $subindices) { - $indices[$type] = array_slice($subindices, $this->getOffset(), $this->getLimit()); - } - } - - /** - * Register the given function for grouping the result - * - * @param String $fn The function to use for grouping - * @param Object $scope An optional scope to use instead of $this - * - * @return self Fluent interface - */ - public function groupByFunction($fn, $scope = null) - { - $this->groupByFn = array($scope ? $scope : $this, $fn); - return $this; - } - - /** - * Group by the given column - * - * @param array|string $columns The columns to use for grouping - * @return self Fluent interface - * @see Query::columnGroupFn() The implementation used for grouping - */ - public function groupByColumns($columns) - { - if (!is_array($columns)) { - $columns = array($columns); - } - $this->groupColumns = $columns; - $this->groupByFn = array($this, 'columnGroupFn'); - return $this; - } - - /** - * The internal handler function used by the group function - * - * @param array $indices The indices to group - * @return array The grouped result set - */ - private function columnGroupFn(array &$indices) - { - $cols = $this->groupColumns; - $result = array(); - foreach ($indices as $type => $subindices) { - foreach ($subindices as $objectIndex) { - $r = $this->ds->getObjectByName($type, $objectIndex); - $hash = ''; - $cols = array(); - foreach ($this->groupColumns as $col) { - $hash = md5($hash . $r->$col); - $cols[$col] = $r->$col; - } - if (!isset($result[$hash])) { - $result[$hash] = (object)array( - 'columns' => (object)$cols, - 'count' => 0 - ); - } - $result[$hash]->count++; - } - } - return array_values($result); - } - - /** - * Query Filter, Order, Group, Limit and return the result set - * - * @return array The resultset matching this query - */ - public function getResult() - { - $indices = $this->getFilteredIndices(); - $this->orderIndices($indices); - if ($this->groupByFn) { - $scope = $this->groupByFn[self::FN_SCOPE]; - $fn = $this->groupByFn[self::FN_NAME]; - - return $scope->$fn($indices); - } - - $this->limitIndices($indices); - - $result = array(); - $state = $this->ds->getState(); - - foreach ($indices as $type => $subindices) { - foreach ($subindices as $index) { - $result[] = & $state[$type][$index]; - } - } - return $result; - } - - - /** - * Apply all filters of this filterable on the datasource - */ - public function applyFilter() - { - $parser = new TreeToStatusdatQueryParser(); - if ($this->getFilter()) { - $query = $parser->treeToQuery($this->getFilter(), $this); - $this->setQueryFilter($query); - } - - } - - /** - * Return only the first row fetched from the result set - * - * @return MonitoringObjectList The monitoring object matching this query - */ - public function fetchRow() - { - $rs = $this->fetchAll(); - $rs->rewind(); - return $rs->current(); - } - - /** - * Fetch the result as an associative array using the first column as the key and the second as the value - * - * @return array An associative array with the result - * @throws IcingaException If no attributes are defined - */ - public function fetchPairs() - { - $result = array(); - if (count($this->getColumns()) < 2) { - throw new IcingaException( - 'Status.dat "fetchPairs()" query expects at least columns to be set in the query expression' - ); - } - $attributes = $this->getColumns(); - - $param1 = $attributes[0]; - $param2 = $attributes[1]; - foreach ($this->fetchAll() as $resultList) { - $result[$resultList->$param1] = $resultList->$param2; - } - - return $result; - } - - /** - * Fetch all results - * - * @return MonitoringObjectList An MonitoringObjectList wrapping the given resultset - */ - public function fetchAll() - { - $this->applyFilter(); - if (!isset($this->cursor)) { - $result = $this->getResult(); - $this->cursor = new MonitoringObjectList($result, $this); - } - return $this->cursor; - } - - /** - * Return the value of the first column for the first row fetched from the result set - */ - public function fetchOne() - { - throw new ProgrammingError('Statusdat/Query::fetchOne() is not implemented yet'); - } - - /** - * Count the number of results - * - * @return int - */ - public function count() - { - $q = clone $this; - $q->limit(null, null); - return count($q->fetchAll()); - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query/Expression.php b/library/Icinga/Protocol/Statusdat/Query/Expression.php deleted file mode 100644 index 1db48df33d..0000000000 --- a/library/Icinga/Protocol/Statusdat/Query/Expression.php +++ /dev/null @@ -1,415 +0,0 @@ -": - $this->CB = "isGreater"; - break; - case "<": - $this->CB = "isLess"; - break; - case ">=": - $this->CB = "isGreaterEq"; - break; - case "<=": - $this->CB = "isLessEq"; - break; - case "=": - $this->CB = "isEqual"; - break; - case "LIKE": - $this->CB = "isLike"; - break; - case "NOT_LIKE": - $this->CB = "isNotLike"; - break; - case "!=": - $this->CB = "isNotEqual"; - break; - case "IN": - $this->CB = "isIn"; - break; - case "NOT_IN": - $this->CB = "isNotIn"; - break; - default: - throw new IcingaException( - 'Unknown operator %s in expression %s !', - $token, - $this->expression - ); - } - } - - /** - * @param $tokens - * @return mixed - */ - private function extractAggregationFunction(&$tokens) - { - $token = $tokens[0]; - $value = array(); - if (preg_match("/COUNT\{(.*)\}/", $token, $value) == false) { - return $token; - } - $this->function = "count"; - $tokens[0] = $value[1]; - - return null; - } - - /** - * @param $values - */ - private function parseExpression(&$values) - { - $tokenized = preg_split("/ +/", trim($this->expression), 3); - $this->extractAggregationFunction($tokenized); - if (count($tokenized) != 3) { - echo( - "Currently statusdat query expressions must be in " - . "the format FIELD OPERATOR ? or FIELD OPERATOR :value_name" - ); - } - - $this->fields = explode(".", trim($tokenized[0])); - $this->field = $this->fields[count($this->fields) - 1]; - - $this->getOperatorType(trim($tokenized[1])); - $tokenized[2] = trim($tokenized[2]); - - if ($tokenized[2][0] === ":") { - $this->name = substr($tokenized, 1); - $this->value = $values[$this->name]; - } else { - if ($tokenized[2] === "?") { - $this->value = array_shift($values); - } else { - $this->value = trim($tokenized[2]); - } - } - - } - - /** - * @param $expression - * @param $values - * @return $this - */ - public function fromString($expression, &$values) - { - $this->expression = $expression; - $this->parseExpression($values); - return $this; - } - - /** - * @param null $expression - * @param array $values - */ - public function __construct($expression = null, &$values = array()) - { - if ($expression) { - if (!is_array($values)) { - $values = array($values); - } - $this->fromString($expression, $values); - } - - } - - /** - * @param array $base - * @param array $idx - * @return array|mixed - */ - public function filter(array &$base, &$idx = array()) - { - if (!$idx) { - $idx = array_keys($base); - } - $this->basedata = $base; - return array_filter($idx, array($this, "filterFn")); - } - - /** - * @return string - */ - public function getValue() - { - return $this->value; - } - - /** - * @return null - */ - public function getField() - { - return $this->field; - } - - /** - * @param $idx - * @return bool - */ - protected function filterFn($idx) - { - $values = $this->getFieldValues($idx); - - if ($values === false) { - return false; - } - - if ($this->CB == "isIn" || $this->CB == "isNotIn") { - $cmpValues = is_array($this->value) ? $this->value : array($this->value); - foreach ($cmpValues as $cmpValue) { - $this->value = $cmpValue; - foreach ($values as $value) { - if ($this->CB == "isIn" && $this->isLike($value)) { - $this->value = $cmpValues; - return true; - } elseif ($this->CB == "isNotIn" && $this->isNotLike($value)) { - $this->value = $cmpValues; - return true; - } - } - } - $this->value = $cmpValues; - return false; - } - - if ($this->function) { - $values = call_user_func($this->function, $values); - if (!is_array($values)) { - $values = array($values); - } - } - foreach ($values as $val) { - - if (!is_string($val) && !is_numeric($val) && is_object($val)) { - if (isset($val->service_description)) { - $val = $val->service_description; - } elseif (isset($val->host_name)) { - $val = $val->host_name; - } else { - return false; - } - } - if ($this->{$this->CB}($val)) { - return true; - } - } - return false; - } - - /** - * @param $idx - * @return array - */ - private function getFieldValues($idx) - { - $res = $this->basedata[$idx]; - - foreach ($this->fields as $field) { - if (!is_array($res)) { - if ($this->query) { - $res = $this->query->get($res, $field); - continue; - } - - if (!isset($res->$field)) { - $res = array(); - break; - } - $res = $res->$field; - continue; - } - - // it can be that an element contains more than one value, like it - // happens when using comments, in this case we have to create a new - // array that contains the values/objects we're searching - $swap = array(); - foreach ($res as $sub) { - if ($this->query) { - $swap[] = $this->query->get($sub, $field); - continue; - } - if (!isset($sub->$field)) { - continue; - } - if (!is_array($sub->$field)) { - $swap[] = $sub->$field; - } else { - $swap = array_merge($swap, $sub->$field); - } - } - $res = $swap; - } - if (!is_array($res)) { - return array($res); - } - - return $res; - } - - /** - * @param $value - * @return bool - */ - public function isGreater($value) - { - return $value > $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isLess($value) - { - return $value < $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isLike($value) - { - return preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false; - } - - /** - * @param $value - * @return bool - */ - public function isNotLike($value) - { - return !preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false; - } - - /** - * @param $value - * @return bool - */ - public function isEqual($value) - { - if (!is_numeric($value)) { - return strtolower($value) == strtolower($this->value); - } - return $value == $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isNotEqual($value) - { - return $value != $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isGreaterEq($value) - { - return $value >= $this->value; - } - - /** - * @param $value - * @return bool - */ - public function isLessEq($value) - { - return $value <= $this->value; - } - - /** - * Add additional information about the query this filter belongs to - * - * @param $query - * @return mixed - */ - public function setQuery($query) - { - $this->query = $query; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query/Group.php b/library/Icinga/Protocol/Statusdat/Query/Group.php deleted file mode 100644 index 2e110843fc..0000000000 --- a/library/Icinga/Protocol/Statusdat/Query/Group.php +++ /dev/null @@ -1,397 +0,0 @@ -value = $value; - } - - /** - * @return array - */ - public function getItems() - { - return $this->items; - } - - /** - * @return string - */ - public function getType() - { - return $this->type ? $this->type : self::TYPE_AND; - } - - /** - * @param $type - */ - public function setType($type) - { - $this->type = $type; - } - - /** - * @throws IcingaException - */ - private function tokenize() - { - $token = 0; - $subgroupCount = 0; - while ($token != self::EOF) { - - $token = $this->getNextToken(); - - if ($token === self::GROUP_BEGIN) { - - /** - * check if this is a nested group, if so then it's - * considered part of the subexpression - */ - if ($subgroupCount == 0) { - $this->startNewSubExpression(); - } - $subgroupCount++; - continue; - } - if ($token === self::GROUP_END) { - if ($subgroupCount < 1) { - throw new IcingaException( - 'Invalid Query: unexpected \')\' at pos %s', - $this->parsePos - ); - } - $subgroupCount--; - /* - * check if this is a nested group, if so then it's - * considered part of the subexpression - */ - if ($subgroupCount == 0) { - $this->addSubgroupFromExpression(); - } - continue; - } - - if ($token === self::CONJUNCTION_AND && $subgroupCount == 0) { - $this->startNewSubExpression(); - if ($this->type != self::TYPE_AND && $this->type != "") { - $this->createImplicitGroup(self::TYPE_AND); - break; - } else { - $this->type = self::TYPE_AND; - } - continue; - } - if ($token === self::CONJUNCTION_OR && $subgroupCount == 0) { - $this->startNewSubExpression(); - if ($this->type != self::TYPE_OR && $this->type != "") { - $this->createImplicitGroup(self::TYPE_OR); - break; - } else { - - $this->type = self::TYPE_OR; - } - continue; - } - - $this->subExpressionLength = $this->parsePos - $this->subExpressionStart; - } - if ($subgroupCount > 0) { - throw new IcingaException('Unexpected end of query, are you missing a parenthesis?'); - } - - $this->startNewSubExpression(); - } - - /** - * @param $type - */ - private function createImplicitGroup($type) - { - $group = new Group(); - $group->setType($type); - $group->addItem(array_pop($this->items)); - - $group->fromString(substr($this->expression, $this->parsePos), $this->value, $this->expressionClass); - $this->items[] = $group; - $this->parsePos = strlen($this->expression); - - } - - /** - * - */ - private function startNewSubExpression() - { - if ($this->getCurrentSubExpression() != "") { - if (!$this->expressionClass) { - $this->items[] = new Expression($this->getCurrentSubExpression(), $this->value); - } else { - $this->items[] = new $this->expressionClass($this->getCurrentSubExpression(), $this->value); - } - } - - $this->subExpressionStart = $this->parsePos; - $this->subExpressionLength = 0; - } - - /** - * @return string - */ - private function getCurrentSubExpression() - { - - return substr($this->expression, $this->subExpressionStart, $this->subExpressionLength); - } - - /** - * - */ - private function addSubgroupFromExpression() - { - - if (!$this->expressionClass) { - $this->items[] = new Group($this->getCurrentSubExpression(), $this->value); - } else { - $group = new Group(); - $group->fromString($this->getCurrentSubExpression(), $this->value, $this->expressionClass); - $this->items[] = $group; - } - $this->subExpressionStart = $this->parsePos; - $this->subExpressionLength = 0; - } - - /** - * @return bool - */ - private function isEOF() - { - if ($this->parsePos >= strlen($this->expression)) { - return true; - } - return false; - } - - /** - * @return int|string - */ - private function getNextToken() - { - if ($this->isEOF()) { - return self::EOF; - } - - // skip whitespaces - while ($this->expression[$this->parsePos] == " ") { - $this->parsePos++; - if ($this->isEOF()) { - return self::EOF; - } - } - if ($this->expression[$this->parsePos] == self::GROUP_BEGIN) { - $this->parsePos++; - return self::GROUP_BEGIN; - } - if ($this->expression[$this->parsePos] == self::GROUP_END) { - $this->parsePos++; - return self::GROUP_END; - } - if (substr_compare( - $this->expression, - self::CONJUNCTION_AND, - $this->parsePos, - strlen(self::CONJUNCTION_AND), - true - ) === 0) { - $this->parsePos += strlen(self::CONJUNCTION_AND); - return self::CONJUNCTION_AND; - } - if (substr_compare( - $this->expression, - self::CONJUNCTION_OR, - $this->parsePos, - strlen(self::CONJUNCTION_OR), - true - ) === 0) { - $this->parsePos += strlen(self::CONJUNCTION_OR); - return self::CONJUNCTION_OR; - } - $this->parsePos++; - return self::EXPRESSION; - } - - /** - * @param $ex - * @return $this - */ - public function addItem($ex) - { - $this->items[] = $ex; - return $this; - } - - /** - * @param $expression - * @param array $value - * @param null $expressionClass - * @return $this - */ - public function fromString($expression, &$value = array(), $expressionClass = null) - { - $this->expression = $expression; - $this->value = & $value; - $this->expressionClass = $expressionClass; - - $this->tokenize(); - return $this; - } - - /** - * @param null $expression - * @param array $value - */ - public function __construct($expression = null, &$value = array()) - { - if ($expression) { - $this->fromString($expression, $value); - } - } - - /** - * @param array $base - * @param null $idx - * @return array|null - */ - public function filter(array &$base, &$idx = null) - { - if ($this->type == self::TYPE_OR) { - $idx = array(); - foreach ($this->items as &$subFilter) { - $baseKeys = array_keys($base); - $subFilter->setQuery($this->query); - $idx += $subFilter->filter($base, $baseKeys); - } - } else { - if (!$idx) { - $idx = array_keys($base); - } - foreach ($this->items as $subFilter) { - $subFilter->setQuery($this->query); - $idx = array_intersect($idx, $subFilter->filter($base, $idx)); - } - } - - return $idx; - } - - /** - * Add additional information about the query this filter belongs to - * - * @param $query - * @return mixed - */ - public function setQuery($query) - { - $this->query = $query; - } -} diff --git a/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php b/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php deleted file mode 100644 index 1dcd434df8..0000000000 --- a/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php +++ /dev/null @@ -1,36 +0,0 @@ -noCache = $noCache; - if (isset($config->no_cache)) { - $this->noCache = $config->no_cache; - } - $this->config = $config; - $this->parser = $parser; - - if (!$this->noCache) { - $this->cache = $this->initializeCaches($config); - if ($this->fromCache()) { - $this->createHostServiceConnections(); - return; - } - } - - if (!$this->lastState) { - $this->parseObjectsCacheFile(); - } - if (!$this->hasRuntimeState) { - - } - $this->parseStatusDatFile(); - if (!$noCache && $this->newState) { - $this->statusCache->save($this->parser->getRuntimeState(), 'object' . md5($this->config->object_file)); - } - $this->createHostServiceConnections(); - - } - - /** - * Initialize the internal caches if enabled - * - * @throws ConfigurationError - */ - private function initializeCaches() - { - $defaultCachePath = self::STATUSDAT_DEFAULT_CACHE_PATH; - $cachePath = $this->config->get('cache_path', $defaultCachePath); - $maxCacheLifetime = intval($this->config->get('cache_path', self::DEFAULT_CACHE_LIFETIME)); - $cachingEnabled = true; - if (!is_writeable($cachePath)) { - Logger::warning( - 'Can\'t cache Status.dat backend; make sure cachepath %s is writable by the web user. ' - . 'Caching is now disabled', - $cachePath - ); - $cachePath = null; - } - $backendOptions = array( - 'cache_dir' => $cachePath - ); - // the object cache might exist for months and is still valid - $this->objectCache = $this->initCache($this->config->object_file, $backendOptions, null, $cachingEnabled); - $this->statusCache = $this->initCache( - $this->config->status_file, - $backendOptions, - $maxCacheLifetime, - $cachingEnabled - ); - } - - /** - * Init the Cache backend in Zend - * - * @param String $file The file to use as the cache master file - * @param Zend_Config $backend The backend configuration to use - * @param integer $lifetime The lifetime of the cache - * - * @return \Zend_Cache_Core|\Zend_Cache_Frontend - */ - private function initCache($file, $backendConfig, $lifetime) - { - $frontendOptions = array( - 'lifetime' => $lifetime, - 'automatic_serialization' => true, - 'master_files' => array($file) - ); - return \Zend_Cache::factory('Core', 'File', $frontendOptions, $backendConfig); - } - - /** - * Read the current cache state - * - * @return bool True if the state is the same as the icinga state - */ - private function fromCache() - { - if (!$this->readObjectsCache()) { - $this->newState = true; - return false; - } - if (!$this->readStatusCache()) { - $this->newState = true; - return false; - } - return true; - } - - /** - * Read the object.cache file from the Zend_Cache backend - * - * @return bool True if the file could be loaded from cache - */ - private function readObjectsCache() - { - $this->lastState = $this->objectCache->load('object' . md5($this->config->object_file)); - if ($this->lastState == false) { - return false; - } - - return true; - } - - /** - * Read the status.dat file from the Zend_Cache backend - * - * @return bool True if the file could be loaded from cache - */ - private function readStatusCache() - { - if (!isset($this->stateCache)) { - return true; - } - $statusInfo = $this->stateCache->load('state' . md5($this->config->status_file)); - if ($statusInfo == false) { - return false; - } - - $this->hasRuntimeState = true; - return true; - } - - /** - * Take the status.dat and objects.cache and connect all services to hosts - * - */ - private function createHostServiceConnections() - { - if (!isset($this->lastState["service"])) { - return; - } - foreach ($this->lastState["host"] as &$host) { - $host->host = $host; - } - foreach ($this->lastState["service"] as &$service) { - $service->service = &$service; // allow easier querying - $host = &$this->lastState["host"][$service->host_name]; - if (!isset($host->services)) { - $host->services = array(); - } - $host->services[$service->service_description] = & $service; - $service->host = & $host; - } - } - - /** - * Parse the object.cache file and update the current state - * - * @throws ConfigurationError If the object.cache couldn't be read - */ - private function parseObjectsCacheFile() - { - if (!is_readable($this->config->object_file)) { - throw new ConfigurationError( - 'Can\'t read object-file "%s", check your configuration', - $this->config->object_file - ); - } - if (!$this->parser) { - $this->parser = new Parser(new File($this->config->object_file, 'r')); - } - $this->parser->parseObjectsFile(); - $this->lastState = $this->parser->getRuntimeState(); - } - - /** - * Parse the status.dat file and update the current state - * - * @throws ConfigurationError If the status.dat couldn't be read - */ - private function parseStatusDatFile() - { - if (!is_readable($this->config->status_file)) { - throw new ConfigurationError( - 'Can\'t read status-file %s, check your configuration', - $this->config->status_file - ); - } - if (!$this->parser) { - $this->parser = new Parser(new File($this->config->status_file, 'r'), $this->lastState); - } - $this->parser->parseRuntimeState(new File($this->config->status_file, 'r')); - $this->lastState = $this->parser->getRuntimeState(); - if (!$this->noCache) { - $this->statusCache->save(array("true" => true), "state" . md5($this->config->object_file)); - } - } - - /** - * Create a new Query - * - * @return Query The query to operate on - */ - public function select() - { - return new Query($this); - } - - /** - * Return the internal state of the status.dat - * - * @return mixed The internal status.dat representation - */ - public function getState() - { - return $this->lastState; - } - - - /** - * Return the object with the given name and type - * - * @param String $type The type of the object to return (service, host, servicegroup...) - * @param String $name The name of the object - * - * @return ObjectContainer An object container wrapping the result or null if the object doesn't exist - */ - public function getObjectByName($type, $name) - { - if (isset($this->lastState[$type]) && isset($this->lastState[$type][$name])) { - return new ObjectContainer($this->lastState[$type][$name], $this); - } - return null; - } - - /** - * Get an array containing all names of monitoring objects with the given type - * - * @param String $type The type of object to get the names for - * @return array An array of names or null if the type does not exist - */ - public function getObjectNames($type) - { - return isset($this->lastState[$type]) ? array_keys($this->lastState[$type]) : null; - } -} diff --git a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php b/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php deleted file mode 100644 index b15dfe4bb7..0000000000 --- a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php +++ /dev/null @@ -1,72 +0,0 @@ -runtimeState = $str; - } - - /** - * Return true if the argument exists - * - * @param String $attr The argument to retrieve - * @return bool True if it exists, otherwise false - */ - public function __isset($attr) - { - try { - $this->__get($attr); - return true; - } catch (\InvalidArgumentException $e) { - return false; - } - } - - /** - * Return the given attribute - * - * If the container string is not yet parsed, this will happen here - * - * @param String $attr The attribute to retrieve - * @return mixed The value of the attribute - * @throws \InvalidArgumentException When the attribute does not exist - */ - public function __get($attr) - { - $start = strpos($this->runtimeState, $attr . "="); - if ($start === false) { - throw new \InvalidArgumentException("Unknown property $attr"); - } - - $start += strlen($attr . "="); - $len = strpos($this->runtimeState, "\n", $start) - $start; - $this->$attr = trim(substr($this->runtimeState, $start, $len)); - - return $this->$attr; - } -} diff --git a/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php b/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php deleted file mode 100644 index 4e4bc76a1d..0000000000 --- a/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php +++ /dev/null @@ -1,42 +0,0 @@ -dataSet = $dataset; - $this->position = 0; - $this->dataView = $dataView; - } - - public function count() - { - return count($this->dataSet); - } - - public function setPosition($pos) - { - $this->position = $pos; - } - - /** - * (PHP 5 >= 5.0.0)
- * Return the current element - * @link http://php.net/manual/en/iterator.current.php - * @return mixed Can return any type. - */ - public function current() - { - if ($this->dataView) { - return $this; - } - - return $this->dataSet[$this->position]; - } - - /** - * (PHP 5 >= 5.0.0)
- * Move forward to next element - * @link http://php.net/manual/en/iterator.next.php - * @return void Any returned value is ignored. - */ - public function next() - { - $this->position++; - } - - /** - * (PHP 5 >= 5.0.0)
- * Return the key of the current element - * @link http://php.net/manual/en/iterator.key.php - * @return mixed scalar on success, or null on failure. - */ - public function key() - { - return $this->position; - } - - /** - * (PHP 5 >= 5.0.0)
- * Checks if current position is valid - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean The return value will be casted to boolean and then evaluated. - * Returns true on success or false on failure. - */ - public function valid() - { - return $this->position < count($this->dataSet); - } - - /** - * (PHP 5 >= 5.0.0)
- * Rewind the Iterator to the first element - * @link http://php.net/manual/en/iterator.rewind.php - * @return void Any returned value is ignored. - */ - public function rewind() - { - $this->position = 0; - } - - public function __isset($name) - { - return $this->dataView->exists($this->dataSet[$this->position], $name); - } - - public function __get($name) - { - return $this->dataView->get($this->dataSet[$this->position], $name); - } - - public function __set($name, $value) - { - throw new IcingaException('Setting is currently not available for objects'); - } - - public function offsetExists($offset) - { - return count($this->dataSet) < $offset; - } - - public function offsetGet($offset) - { - $res = new MonitoringObjectList($this->dataSet, $this->dataView); - $res->position = $offset; - return $res; - } - - public function offsetSet($offset, $value) - { - // non mutable - } - - public function offsetUnset($offset) - { - // non mutable - } -} diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index fa05cf7e3a..079ea538a3 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -45,8 +45,8 @@ public function setResourceConfig(Config $resourceConfig) { $resources = array(); foreach ($resourceConfig as $name => $resource) { - if ($resource->type === 'db' || $resource->type === 'statusdat' || $resource->type === 'livestatus') { - $resources[$resource->type === 'db' ? 'ido' : strtolower($resource->type)][$name] = $name; + if ($resource->type === 'db' || $resource->type === 'livestatus') { + $resources[$resource->type === 'db' ? 'ido' : 'livestatus'][$name] = $name; } } @@ -192,9 +192,6 @@ public function createElements(array $formData) if ($resourceType === 'ido' || array_key_exists('ido', $this->resources)) { $resourceTypes['ido'] = 'IDO Backend'; } - if ($resourceType === 'statusdat' || array_key_exists('statusdat', $this->resources)) { - $resourceTypes['statusdat'] = 'Status.dat'; - } if ($resourceType === 'livestatus' || array_key_exists('livestatus', $this->resources)) { $resourceTypes['livestatus'] = 'Livestatus'; } diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php deleted file mode 100644 index 27b554ec8d..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/CommentQuery.php +++ /dev/null @@ -1,83 +0,0 @@ - 'comment_id', - 'comment_internal_id' => 'comment_id', - 'comment_data' => 'comment_data', - 'comment_author' => 'author', - 'comment_timestamp' => 'entry_time', - 'comment_is_persistent' => 'persistent', - 'host_name' => 'host_name', - 'host' => 'host_name', - ); - - public static $handlerParameters = array( - 'comment_objecttype_id' => 'getCommentObjectType', - 'comment_type' => 'getCommentType', - 'comment_expiration_timestamp' => 'getExpirationTime', - 'service' => 'getServiceDescription', - 'service_name' => 'getServiceDescription', - 'service_description' => 'getServiceDescription' - ); - - public function getServiceDescription(&$obj) - { - if (isset($obj->service_description)) { - return $obj->service_description; - } - return ''; - } - - public function getExpirationTime(&$obj) - { - if ($obj->expires) { - return $obj->expire_time; - } else { - return null; - } - } - - public function getCommentObjectType(&$obj) - { - if (isset($obj->service_description)) { - return 2; - } else { - return 1; - } - } - - public function getCommentType(&$obj) - { - switch ($obj->entry_type) { - case 1: - return 'comment'; - case 2: - return 'downtime'; - case 3: - return 'flapping'; - case 4: - return 'ack'; - } - return ''; - - } - - public function getObjectType(&$obj) - { - return isset($obj->service_description) ? 'service ': 'host'; - } - - public function selectBase() - { - $this->select()->from("comments", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php deleted file mode 100644 index d4ea895e37..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactQuery.php +++ /dev/null @@ -1,133 +0,0 @@ - 'contact_name', - 'contact_alias' => 'alias', - 'contact_email' => 'email', - 'contact_pager' => 'pager', - 'contact_has_host_notfications' => 'host_notifications_enabled', - 'contact_has_service_notfications' => 'service_notifications_enabled', - 'contact_can_submit_commands' => 'can_submit_commands', - 'contact_notify_host_timeperiod' => 'host_notification_period', - 'contact_notify_service_timeperiod'=> 'service_notification_period', - 'contact_service_notification_options' => 'service_notification_options', - 'contact_host_notification_options' => 'host_notification_options', - 'contactgroup_name' => 'group.contactgroup_name', - 'contactgroup_alias' => 'group.alias' - ); - - public static $handlerParameters = array( - 'contact_notify_service_recovery' => 'getServiceRecoveryFlag', - 'contact_notify_service_warning' => 'getServiceWarningFlag', - 'contact_notify_service_critical' => 'getServiceCriticalFlag', - 'contact_notify_service_unknown' => 'getServiceUnknownFlag', - 'contact_notify_service_flapping' => 'getServiceFlappingFlag', - 'contact_notify_service_downtime' => 'getServiceDowntimeFlag', - 'contact_notify_host_recovery' => 'getHostRecoveryFlag', - 'contact_notify_host_down' => 'getHostDownFlag', - 'contact_notify_host_unreachable' => 'getHostUnreachableFlag', - 'contact_notify_host_flapping' => 'getHostFlappingFlag', - 'contact_notify_host_downtime' => 'getHostDowntimeFlag', - 'host_name' => 'getHost', - 'host' => 'getHost', - 'service_host_name' => 'getHost', - 'service' => 'getService', - 'service_description' => 'getService' - ); - - - public function getHost(&$obj) - { - $result = array(); - foreach ($this->state['host'] as $values) { - if (!isset($values->contacts)) { - continue; - } - if (stripos($values->contacts, $obj->contacts) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function getService(&$obj) - { - $result = array(); - foreach ($this->state['service'] as $values) { - if (!isset($values->contacts)) { - continue; - } - if (stripos($values->contact_groups, $obj->contacts) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function getServiceRecoveryFlag(&$obj) - { - return stripos($obj->service_notification_options, 'r') === false ? 0 : 1; - } - - public function getServiceWarningFlag(&$obj) - { - return stripos($obj->service_notification_options, 'w') === false ? 0 : 1; - } - - public function getServiceCriticalFlag(&$obj) - { - return stripos($obj->service_notification_options, 'c') === false ? 0 : 1; - } - - public function getServiceUnknownFlag(&$obj) - { - return stripos($obj->service_notification_options, 'u') === false ? 0 : 1; - } - - public function getServiceFlappingFlag(&$obj) - { - return stripos($obj->service_notification_options, 'f') === false ? 0 : 1; - } - - public function getServiceDowntimeFlag(&$obj) - { - return stripos($obj->service_notification_options, 's') === false ? 0 : 1; - } - - public function getHostRecoveryFlag(&$obj) - { - return stripos($obj->host_notification_options, 'r') === false ? 0 : 1; - } - - public function getHostDownFlag(&$obj) - { - return stripos($obj->host_notification_options, 'd') === false ? 0 : 1; - } - - public function getHostUnreachableFlag(&$obj) - { - return stripos($obj->host_notification_options, 'u') === false ? 0 : 1; - } - - public function getHostFlappingFlag(&$obj) - { - return stripos($obj->host_notification_options, 'f') === false ? 0 : 1; - } - - public function getHostDowntimeFlag(&$obj) - { - return strpos($obj->host_notification_options, 's') === false ? 0 : 1; - } - - public function selectBase() - { - $this->state = $this->ds->getState(); - $this->select()->from("contacts", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php deleted file mode 100644 index 11b7f4b44f..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ContactgroupQuery.php +++ /dev/null @@ -1,61 +0,0 @@ - 'contactgroup_name', - 'contactgroup_alias' => 'alias', - 'contact_name' => 'contact.contact_name', - 'contact_alias' => 'contact.alias', - 'contact_email' => 'contact.email', - 'contact_pager' => 'contact.pager', - 'contact_has_host_notfications' => 'contact.host_notifications_enabled', - 'contact_has_service_notfications' => 'contact.service_notifications_enabled', - 'contact_can_submit_commands' => 'contact.can_submit_commands', - 'contact_notify_host_timeperiod' => 'contact.host_notification_period', - 'contact_notify_service_timeperiod' => 'contact.service_notification_period', - 'contact_service_notification_options' => 'contact.service_notification_options', - 'contact_host_notification_options' => 'contact.host_notification_options', - ); - - public static $handlerParameters = array( - 'host_name' => 'getHosts', - 'host' => 'getHosts', - 'service_host_name' => 'getHosts', - 'service' => 'getService', - 'service_description' => 'getService' - ); - - public function getHosts(&$obj) - { - $result = array(); - foreach ($this->state['host'] as $values) { - if (stripos($values->contact_groups, $obj->contactgroup_name) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function getService(&$obj) - { - $result = array(); - foreach ($this->state['service'] as $values) { - if (stripos($values->contact_groups, $obj->contactgroup_name) !== false) { - $result[] = $values; - } - } - return $result; - } - - public function selectBase() - { - $this->state = $this->ds->getState(); - return $this->select()->from("contactgroups", array()); - - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php deleted file mode 100644 index d0d8e1eaac..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php +++ /dev/null @@ -1,79 +0,0 @@ - 'author', - 'downtime_comment' => 'comment', - 'downtime_duration' => 'duration', - 'downtime_end' => 'end_time', - 'downtime_was_started' => 'was_started', - 'downtime_is_fixed' => 'fixed', - 'downtime_is_in_effect' => 'is_in_effect', - 'downtime_trigger_time' => 'trigger_time', - 'downtime_triggered_by_id' => 'triggered_by_id', - 'downtime_internal_downtime_id' => 'downtime_id', - 'downtime_scheduled_start_time' => 'start_time', - 'host' => 'host_name', - 'host_name' => 'host_name', - 'service_host_name' => 'host_name' - ); - - public static $handlerParameters = array( - 'object_type' => 'getObjectType', - 'downtime_start' => 'getDowntimeStart', - 'downtime_is_flexible' => 'getFlexibleFlag', - 'service_description' => 'getServiceDescription' - ); - - public static $fieldTypes = array( - 'downtime_end' => self::TIMESTAMP, - 'downtime_trigger_time' => self::TIMESTAMP, - 'downtime_start' => self::TIMESTAMP - ); - - public function getServiceDescription(&$obj) - { - if (isset ($obj->service_description)) { - return $obj->service_description; - } - return ''; - } - - - public function getDowntimeStart(&$obj) - { - if ($obj->trigger_time != '0') { - return $obj->trigger_time; - } else { - return $obj->start_time; - } - } - - - public function getFlexibleFlag(&$obj) - { - return $obj->fixed ? 0 : 1; - } - - public function getObjectType(&$obj) - { - return isset($obj->service_description) ? 'service ': 'host'; - } - - public function selectBase() - { - $this->select()->from("downtimes", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php deleted file mode 100644 index 1168d6da3c..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/GroupsummaryQuery.php +++ /dev/null @@ -1,248 +0,0 @@ - 'hostgroup_name', - 'servicegroup' => 'servicegroup_name' - ); - - public static $handlerParameters = array( - 'hosts_up' => 'getHostUpSum', - 'hosts_unreachable_handled' => 'getHostUnreachableSum', - 'hosts_unreachable_unhandled' => 'getHostUnreachableUnhandledSum', - 'hosts_down_handled' => 'getHostDownSum', - 'hosts_down_unhandled' => 'getHostDownUnhandledSum', - 'hosts_pending' => 'getHostPendingSum', - 'services_ok' => 'getServiceOkSum', - 'services_unknown_handled' => 'getServiceUnknownSum', - 'services_unknown_unhandled' => 'getServiceUnknownUnhandledSum', - 'services_critical_handled' => 'getServiceCriticalSum', - 'services_critical_unhandled' => 'getServiceCriticalUnhandledSum', - 'services_warning_handled' => 'getServiceWarningSum', - 'services_warning_unhandled' => 'getServiceWarningUnhandledSum', - 'services_pending' => 'getServicePendingSum', - - ); - - private function getMembers(&$obj, $hint = null) - { - if (!isset($obj->service) && !isset($obj->host)) { - return null; - } - $memberList = isset($obj->service) ? $obj->service : $obj->host; - - if (isset($obj->host) && $hint == 'service') { - $result = array(); - foreach ($memberList as &$member) { - if (isset($member->services)) { - $result = $result + $member->services; - } - } - return $result; - } - return $memberList; - } - - private function getMembersByCriteria(&$obj, $type, $namefield, $criteriaFn) - { - $memberList = $this->getMembers($obj, $type); - if ($memberList === null) { - return 0; - } - $ids = array(); - foreach ($memberList as $member) { - $name = $member->$type->$namefield; - if ($namefield === 'service_description') { - $name .= ';' . $member->$type->host_name; - } - - if (isset($ids[$name])) { - continue; - } else { - if ($criteriaFn($member->$type)) { - $ids[$name] = true; - } - } - } - return count(array_keys($ids)); - } - - public function getHostUpSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 0 - && $member->status->has_been_checked); - }); - } - - public function getHostUnreachableSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostUnreachableUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostDownSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostDownUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getHostPendingSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'host', 'host_name', function($member) { - return (!$member->status->has_been_checked); - }); - } - - public function getServiceOkSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 0 - && $member->status->has_been_checked); - }); - } - - public function getServiceUnknownSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - - return ($member->status->current_state == 3 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceUnknownUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 3 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceCriticalSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceCriticalUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 2 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceWarningSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && !( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServiceWarningUnhandledSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return ($member->status->current_state == 1 - && $member->status->has_been_checked - && ( - $member->status->problem_has_been_acknowledged - || $member->status->scheduled_downtime_depth - ) - ); - }); - } - - public function getServicePendingSum(&$obj) - { - return $this->getMembersByCriteria($obj, 'service', 'service_description', function($member) { - return (!$member->status->has_been_checked); - }); - } - - private function getTarget() - { - if (in_array('servicegroup', $this->getColumns())) { - return 'servicegroups'; - } - return 'hostgroups'; - } - - public function selectBase() - { - $this->select()->from($this->getTarget(), array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php deleted file mode 100644 index 3855375c27..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php +++ /dev/null @@ -1,25 +0,0 @@ - 'hostgroup_name', - 'hostgroup_name' => 'hostgroup_name', - 'hostgroup_alias' => 'alias', - 'host' => 'host.host_name', - 'host_name' => 'host.host_name' - ); - - public function selectBase() - { - $this->select()->from("hostgroups", array()); - } -} \ No newline at end of file diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php deleted file mode 100644 index 48ee822840..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostlistQuery.php +++ /dev/null @@ -1,33 +0,0 @@ -select()->from("hosts", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php deleted file mode 100644 index 8d3f9a4eca..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php +++ /dev/null @@ -1,28 +0,0 @@ - 'servicegroup_name', - 'servicegroup_name' => 'servicegroup_name', - 'servicegroup_alias' => 'alias', - 'host' => 'service.host_name', - 'host_name' => 'service.host_name', - 'service' => 'service.service_description', - 'service_description'=> 'service.service_description' - - ); - - public function selectBase() - { - $this->select()->from("servicegroups", array()); - } -} \ No newline at end of file diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php deleted file mode 100644 index cd712d8016..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupsummaryQuery.php +++ /dev/null @@ -1,22 +0,0 @@ -select()->from("services", array()); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php deleted file mode 100644 index e78942f4c7..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php +++ /dev/null @@ -1,361 +0,0 @@ - 'host.host_name', - 'host_name' => 'host.host_name', - 'host_display_name' => 'host.host_name', - 'host_alias' => 'host.alias', - 'host_address' => 'host.address', - 'host_icon_image' => 'host.icon_image', - 'host_action_url' => 'host.action_url', - 'host_notes_url' => 'host.notes_url', - 'host_output' => 'host.status.plugin_output', - 'host_long_output' => 'host.status.long_plugin_output', - 'host_perfdata' => 'host.status.performance_data', - 'host_check_source' => 'host.status.check_source', - 'host_acknowledged' => 'host.status.problem_has_been_acknowledged', - 'host_last_state_change' => 'host.status.last_state_change', - 'host_last_hard_state' => 'host.status.last_hard_state', - 'host_last_hard_state_change' => 'host.status.last_hard_state_change', - 'host_check_command' => 'host.status.check_command', - 'host_last_check' => 'host.status.last_check', - 'host_next_check' => 'host.status.next_check', - 'host_check_execution_time' => 'host.status.check_execution_time', - 'host_check_latency' => 'host.status.check_latency', - 'host_notifications_enabled' => 'host.status.notifications_enabled', - 'host_last_time_up' => 'host.status.last_time_up', - 'host_last_time_down' => 'host.status.last_time_down', - 'host_last_time_unreachable' => 'host.status.last_time_unreachable', - 'host_current_check_attempt' => 'host.status.current_attempt', - 'host_max_check_attempts' => 'host.status.max_attempts', - 'host_check_type' => 'host.status.check_type', - 'host_state_type' => 'host.status.state_type', - 'host_last_notification' => 'host.status.last_notification', - 'host_next_notification' => 'host.status.next_notification', - 'host_no_more_notifications' => 'host.status.no_more_notifications', - 'host_problem_has_been_acknowledged' => 'host.status.problem_has_been_acknowledged', - 'host_acknowledgement_type' => 'host.status.acknowledgement_type', - 'host_current_notification_number' => 'host.status.current_notification_number', - 'host_passive_checks_enabled' => 'host.status.passive_checks_enabled', - 'host_active_checks_enabled' => 'host.status.active_checks_enabled', - 'host_event_handler_enabled' => 'host.status.event_handler_enabled', - 'host_flap_detection_enabled' => 'host.status.flap_detection_enabled', - 'host_is_flapping' => 'host.status.is_flapping', - 'host_percent_state_change' => 'host.status.percent_state_change', - 'host_scheduled_downtime_depth' => 'host.status.scheduled_downtime_depth', - 'host_failure_prediction_enabled' => 'host.status.failure_prediction_enabled', - 'host_process_performance_data' => 'host.status.process_performance_data', - 'host_obsessing' => 'host.status.obsess_over_host', - 'host_modified_host_attributes' => 'host.status.modified_host_attributes', - 'host_event_handler' => 'host.status.event_handler', - 'host_check_command' => 'host.status.check_command', - 'host_normal_check_interval' => 'host.status.normal_check_interval', - 'host_retry_check_interval' => 'host.status.retry_check_interval', - 'host_check_timeperiod_object_id' => 'host.status.check_timeperiod_object_id', - 'host_status_update_time' => 'host.status.status_update_time', - - 'service_host_name' => 'service.host_name', - 'service' => 'service.service_description', - 'service_description' => 'service.service_description', - 'service_display_name' => 'service.service_description', - 'service_icon_image' => 'service.icon_image', - 'service_action_url' => 'service.action_url', - 'service_notes_url' => 'service.notes_url', - 'service_state_type' => 'service.status.state_type', - 'service_output' => 'service.status.output', - 'service_long_output' => 'service.status.long_output', - 'service_perfdata' => 'service.status.perfdata', - 'service_check_source' => 'service.status.check_source', - 'service_acknowledged' => 'service.status.problem_has_been_acknowledged', - 'service_last_state_change' => 'service.status.last_state_change', - 'service_check_command' => 'service.status.check_command', - 'service_last_time_ok' => 'service.status.last_time_ok', - 'service_last_time_warning' => 'service.status.last_time_warning', - 'service_last_time_critical' => 'service.status.last_time_critical', - 'service_last_time_unknown' => 'service.status.last_time_unknown', - 'service_current_check_attempt' => 'service.status.current_check_attempt', - 'service_max_check_attempts' => 'service.status.max_check_attempts', - 'service_last_check' => 'service.status.last_check', - 'service_next_check' => 'service.status.next_check', - 'service_check_type' => 'service.status.check_type', - 'service_last_hard_state_change' => 'service.status.last_hard_state_change', - 'service_last_hard_state' => 'service.status.last_hard_state', - 'service_last_notification' => 'service.status.last_notification', - 'service_next_notification' => 'service.status.next_notification', - 'service_no_more_notifications' => 'service.status.no_more_notifications', - 'service_notifications_enabled' => 'service.status.notifications_enabled', - 'service_problem_has_been_acknowledged' => 'service.status.problem_has_been_acknowledged', - 'service_acknowledgement_type' => 'service.status.acknowledgement_type', - 'service_current_notification_number' => 'service.status.current_notification_number', - 'service_passive_checks_enabled' => 'service.status.passive_checks_enabled', - 'service_active_checks_enabled' => 'service.status.active_checks_enabled', - 'service_event_handler_enabled' => 'service.status.event_handler_enabled', - 'service_flap_detection_enabled' => 'service.status.flap_detection_enabled', - 'service_is_flapping' => 'service.status.is_flapping', - 'service_percent_state_change' => 'service.status.percent_state_change', - 'service_check_latency' => 'service.status.latency', - 'service_check_execution_time' => 'service.status.execution_time', - 'service_scheduled_downtime_depth' => 'service.status.scheduled_downtime_depth', - 'service_failure_prediction_enabled' => 'service.status.failure_prediction_enabled', - 'service_process_performance_data' => 'service.status.process_performance_data', - 'service_obsessing' => 'service.status.obsess_over_service', - 'service_modified_service_attributes' => 'service.status.modified_service_attributes', - 'service_event_handler' => 'service.status.event_handler', - 'service_check_command' => 'service.status.check_command', - 'service_normal_check_interval' => 'service.status.normal_check_interval', - 'service_retry_check_interval' => 'service.status.retry_check_interval', - 'service_check_timeperiod_object_id' => 'service.status.check_timeperiod_object_id', - 'service_status_update_time' => 'service.status.status_update_time', - 'hostgroup' => 'host.group', - 'servicegroup' => 'service.group' - ); - - /** - * @var mixed - */ - private $state; - - /** - * @var array - */ - public static $handlerParameters = array( - 'host_ipv4' => 'getAddress', - 'host_unhandled_service_count' => 'getNrOfUnhandledServices', - 'host_last_comment' => 'getLastComment', - 'service_last_comment' => 'getLastComment', - 'host_state' => 'getStateForHost', - 'host_hard_state' => 'getHardStateForHost', - 'host_handled' => 'isHandledForHost', - 'host_unhandled' => 'isHostUnhandled', - 'host_severity' => 'getSeverityForHost', - 'host_in_downtime' => 'isInDowntimeForHost', - 'host_problem' => 'isProblemForHost', - 'host_attempt' => 'getAttemptStringForHost', - 'service_state' => 'getState', - 'service_hard_state' => 'getHardState', - 'service_handled' => 'isHandled', - 'service_unhandled' => 'isUnhandled', - 'service_severity' => 'getSeverity', - 'service_in_downtime' => 'isInDowntime', - 'service_problem' => 'isProblem', - 'service_attempt' => 'getAttemptString', - ); - - public static $fieldTypes = array( - 'host_last_state_change' => self::TIMESTAMP, - 'host_last_hard_state_change' => self::TIMESTAMP, - 'host_last_check' => self::TIMESTAMP, - 'host_next_check' => self::TIMESTAMP, - 'host_last_time_up' => self::TIMESTAMP, - 'host_last_time_down' => self::TIMESTAMP, - 'host_last_time_unreachable' => self::TIMESTAMP, - 'host_status_update_time' => self::TIMESTAMP, - 'service_last_state_change' => self::TIMESTAMP, - 'service_last_hard_state_change' => self::TIMESTAMP, - 'service_last_check' => self::TIMESTAMP, - 'service_next_check' => self::TIMESTAMP, - 'service_last_time_ok' => self::TIMESTAMP, - 'service_last_time_warning' => self::TIMESTAMP, - 'service_last_time_critical' => self::TIMESTAMP, - 'service_last_time_unknown' => self::TIMESTAMP, - 'service_status_update_time' => self::TIMESTAMP - ); - - public function selectBase() - { - $target = $this->getTarget(); - $this->select()->from($target."s", array()); - } - - public function getAttemptString(&$obj) - { - return $obj->status->current_attempt . '/' . $obj->status->max_attempts; - } - - public function isProblem(&$obj) - { - return $obj->status->current_state > 0 ? 1 : 0; - } - - public function isInDowntime(&$obj) - { - return $obj->status->scheduled_downtime_depth > 0 ? 1 : 0; - } - - public function getAddress(&$obj) - { - return inet_pton($obj->host->address); - } - - public function getState(&$obj) - { - if (!$obj->status->has_been_checked) { - return 99; - } - return $obj->status->current_state; - } - - public function getHardState(&$obj) - { - if (!$obj->status->has_been_checked) { - return 99; - } else { - if ($obj->status->state_type == 1) { - return $this->status->current_state; - } else { - return $this->status->last_hard_state; - } - } - } - - - public function getSeverity(&$host) - { - $status = $host->status; - $severity = 0; - - if (!$status->has_been_checked) { - $severity += 16; - } elseif($status->current_state == 0) { - return $severity; - } elseif ($status->current_state == 1) { - $severity += 32; - } elseif ($status->current_state == 2) { - $severity += 64; - } else { - $severity += 256; - } - - if ($status->problem_has_been_acknowledged == 1) { - $severity += 2; - } elseif ($status->scheduled_downtime_depth > 0) { - $severity += 1; - } else { - $severity += 4; - } - - return $severity; - } - - public function isHandled(&$host) - { - return ($host->status->current_state == 0 || - $host->status->problem_has_been_acknowledged == 1 || - $host->status->scheduled_downtime_depth > 0) ? 1 : 0; - } - - public function isUnhandled(&$hostOrService) - { - return +!$this->isHandled($hostOrService); - } - - public function getNrOfUnhandledServices(&$obj) - { - $host = &$obj->host; - $ct = 0; - if (!isset($host->services)) { - return $ct; - } - foreach ($host->services as &$service) { - if ($service->status->current_state > 0 - && $service->status->problem_has_been_acknowledged == 0 - && $service->status->scheduled_downtime_depth == 0) { - $ct++; - } - } - return $ct; - } - - public function getLastComment(&$host) - { - if (!isset($host->comment) || empty($host->comment)) { - return null; - } - $comment = end($host->comment); - return $comment->comment_id; - } - - - /** - * @param $item - * @return null - */ - public function getHost(&$item) - { - if (!isset($this->state['service'][$item->host_name])) { - return null; - } - if (!isset($this->state['host'][$item->host_name])) { - return null; - } - return $this->state['host'][$item->host_name]; - } - - - private function getTarget() - { - foreach ($this->getColumns() as $column) { - if (preg_match("/^service/",$column)) { - return "service"; - } - } - - return "host"; - } - - - public function getStateForHost(&$obj) - { - return $this->getState($obj->host); - } - - public function getHardStateForHost(&$obj) - { - return $this->getHardState($obj->host); - } - - public function isHandledForHost(&$obj) - { - return $this->isHandled($obj->host); - } - - public function isHostUnhandled(&$obj) - { - return $this->isUnhandled($obj->host); - } - - public function getSeverityForHost(&$obj) - { - return $this->getSeverity($obj->host); - } - - public function isInDowntimeForHost(&$obj) - { - return $this->isInDowntime($obj->host); - } - - public function isProblemForHost(&$obj) - { - return $this->isProblem($obj->host); - } - - public function getAttemptStringForHost(&$obj) - { - return $this->getAttemptStringForHost($obj->host); - } - - -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php deleted file mode 100644 index 6f1e87f89c..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusSummaryQuery.php +++ /dev/null @@ -1,115 +0,0 @@ -select()->from('hosts', array())->groupByFunction( - 'groupByStatus', - $this - ); - } - - public function groupByStatus(&$indices) - { - $hostsPending = 0; - $hostsUp = 0; - $hostsDownHandled = 0; - $hostsDownUnhandled = 0; - $hostsUnreachableHandled = 0; - $hostsUnreachableUnhandled = 0; - $servicesPending = 0; - $servicesOk = 0; - $servicesCriticalHandled = 0; - $servicesCriticalUnhandled = 0; - $servicesWarningHandled = 0; - $servicesWarningUnhandled = 0; - $servicesUnknownHandled = 0; - $servicesUnknownUnhandled = 0; - foreach ($indices['host'] as $hostName) { - $host = $this->ds->getObjectByName('host', $hostName); - $hostStatus = $host->status; - if ($hostStatus->has_been_checked !== '1') { - ++$hostsPending; - } elseif ($hostStatus->current_state === '0') { - ++$hostsUp; - } elseif ($hostStatus->current_state === '2') { - if ($hostStatus->problem_has_been_acknowledged === '1' - || $hostStatus->scheduled_downtime_depth === '1' - ) { - ++$hostsDownHandled; - } else { - ++$hostsDownUnhandled; - } - } elseif ($hostStatus->current_state === '1') { - if ($hostStatus->problem_has_been_acknowledged === '1' - || $hostStatus->scheduled_downtime_depth === '1' - ) { - ++$hostsUnreachableHandled; - } else { - ++$hostsUnreachableUnhandled; - } - } - if ($host->services === null) { - // Host does not have any service associated - continue; - } - foreach ($host->services as $service) { - $serviceStatus = $service->status; - if ($serviceStatus->has_been_checked !== '1') { - ++$servicesPending; - } elseif ($serviceStatus->current_state === '0') { - ++$servicesOk; - } elseif ($serviceStatus->current_state === '2') { - if ($serviceStatus->problem_has_been_acknowledged === '1' - || $serviceStatus->scheduled_downtime_depth === '1' - || $hostStatus->current_state !== '0' - ) { - ++$servicesCriticalHandled; - } else { - ++$servicesCriticalUnhandled; - } - } elseif ($serviceStatus->current_state === '1') { - if ($serviceStatus->problem_has_been_acknowledged === '1' - || $serviceStatus->scheduled_downtime_depth === '1' - || $hostStatus->current_state !== '0' - ) { - ++$servicesWarningHandled; - } else { - ++$servicesWarningUnhandled; - } - } elseif ($serviceStatus->current_state === '3') { - if ($serviceStatus->problem_has_been_acknowledged === '1' - || $serviceStatus->scheduled_downtime_depth === '1' - || $hostStatus->current_state !== '0' - ) { - ++$servicesUnknownHandled; - } else { - ++$servicesUnknownUnhandled; - } - } - } - } - $rs = array( - 'hosts_up' => $hostsUp, - 'hosts_unreachable_handled' => $hostsUnreachableHandled, - 'hosts_unreachable_unhandled' => $hostsUnreachableUnhandled, - 'hosts_down_handled' => $hostsDownHandled, - 'hosts_down_unhandled' => $hostsDownUnhandled, - 'hosts_pending' => $hostsPending, - 'services_ok' => $servicesOk, - 'services_unknown_handled' => $servicesUnknownHandled, - 'services_unknown_unhandled' => $servicesUnknownUnhandled, - 'services_critical_handled' => $servicesCriticalHandled, - 'services_critical_unhandled' => $servicesCriticalUnhandled, - 'services_warning_handled' => $servicesWarningHandled, - 'services_warning_unhandled' => $servicesWarningUnhandled, - 'services_pending' => $servicesPending - ); - return array((object) array_intersect_key($rs, array_flip($this->getColumns()))); - } -} diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php deleted file mode 100644 index 33b006c86a..0000000000 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php +++ /dev/null @@ -1,218 +0,0 @@ -selectBase(); - } - - abstract public function selectBase(); - - /** - * Orders the resultset - * - * @param string $column Either a string in the 'FIELD ASC/DESC format or only the field - * @param null $dir 'asc' or 'desc' - * @return Query Returns this query,for fluent interface - */ - public function order($column, $dir = null, $isFunction = false) - { - - if ($column) { - $column = strval($column); - if (isset(static::$mappedParameters[$column])) { - parent::order(static::$mappedParameters[$column], strtolower($dir)); - } elseif (isset(static::$handlerParameters[$column])) { - parent::orderByFn(array($this, static::$handlerParameters[$column]), strtolower($dir)); - } else { - Logger::info("Tried to sort by unknown column %s", $column); - } - } - return $this; - } - - - - private $functionMap = array( - "TO_DATE" => "toDateFormat" - ); - - - - /** - * - * @see Icinga\Backend\DataView\AccessorStrategy - * - * @param The $item - * @param The $field - * @return The|string - * @throws \InvalidArgumentException - */ - public function get(&$item, $field) - { - $result = null; - if (isset($item->$field)) { - $result = $item->$field; - } elseif (isset(static::$mappedParameters[$field])) { - $result = $this->getMappedParameter($item, $field); - } elseif (isset(static::$handlerParameters[$field])) { - $hdl = static::$handlerParameters[$field]; - $result = $this->$hdl($item); - } - - return $result; - } - - private function applyPropertyFunction($function, $value) - { - if (!isset($this->functionMap[$function])) { - return $value; - } - $fn = $this->functionMap[$function]; - - return $this->$fn($value); - } - - private function toDateFormat($value) - { - if (is_numeric($value)) { - return date("Y-m-d H:i:s", intval($value)); - } else { - return $value; - } - } - - private function getMappedParameter(&$item, $field) - { - $matches = array(); - $fieldDef = static::$mappedParameters[$field]; - - $function = false; - if (preg_match_all('/(?P\w+)\((?P.*)\)/', $fieldDef, $matches)) { - $function = $matches["FUNCTION"][0]; - $fieldDef = $matches["PARAMETER"][0]; - } - $mapped = explode(".", $fieldDef); - $res = $item; - - foreach ($mapped as $map) { - if (is_array($res)) { - $subResult = array(); - foreach ($res as $subitem) { - if (!isset($subitem->$map)) { - continue; - } - $subResult[] = $subitem->$map; - } - $res = join(',', $subResult); - } else { - if (!isset($res->$map)) { - return ""; - } - $res = $res->$map; - } - } - - if ($function) { - return $this->applyPropertyFunction($function, $res); - } - return $res; - } - - /** - * - * @see Icinga\Backend\DataView\AccessorStrategy - * - * @param The $field - * @return The|string - */ - public function getMappedField($field) - { - if (isset(static::$mappedParameters[$field])) { - return static::$mappedParameters[$field]; - } - return $field; - } - - /** - * - * @see Icinga\Backend\DataView\AccessorStrategy - * - * @param The $item - * @param The $field - * @return bool - */ - public function exists(&$item, $field) - { - return (isset($item->$field) - || isset(static::$mappedParameters[$field]) - || isset(static::$handlerParameters[$field]) - ); - } - - - public function isValidFilterTarget($field) - { - return true; - } - - public function isTimestamp($field) - { - return isset(static::$fieldTypes[$field]) && static::$fieldTypes[$field] === self::TIMESTAMP; - } - -} - From efffe10bad5c5d7b5993a59fe479806c189d070c Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 7 Oct 2014 15:55:43 +0200 Subject: [PATCH 45/62] Merge sql schema files When populating a database we'll create all tables regardless of what is actually required for the current use case. --- etc/schema/accounts.mysql.sql | 24 ------------------ etc/schema/mysql.sql | 14 +++++++++++ etc/schema/{accounts.pgsql.sql => pgsql.sql} | 26 ++++++++------------ etc/schema/preferences.mysql.sql | 6 ----- etc/schema/preferences.pgsql.sql | 10 -------- 5 files changed, 24 insertions(+), 56 deletions(-) delete mode 100644 etc/schema/accounts.mysql.sql create mode 100644 etc/schema/mysql.sql rename etc/schema/{accounts.pgsql.sql => pgsql.sql} (50%) delete mode 100644 etc/schema/preferences.mysql.sql delete mode 100644 etc/schema/preferences.pgsql.sql diff --git a/etc/schema/accounts.mysql.sql b/etc/schema/accounts.mysql.sql deleted file mode 100644 index 72eeee4fcf..0000000000 --- a/etc/schema/accounts.mysql.sql +++ /dev/null @@ -1,24 +0,0 @@ -create table account ( - `username` varchar(255) COLLATE latin1_general_ci NOT NULL, - `salt` varchar(255) NOT NULL, - `password` varchar(255) NOT NULL, - `active` tinyint(1) DEFAULT NULL, - PRIMARY KEY (`username`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; - -/* - * user: icingaadmin - * password: icinga - */ -INSERT INTO account ( - `username`, - `salt`, - `password`, - `active` - ) - VALUES ( - 'icingaadmin', - '57cfd5746224be4f60c25d4e8514bec35ad2d01810723a138756b285898e71b2', - '43f8e0588eb39f1a41383b48def0b1fdc45e79b8f67194cccee4453eb3f4ea13', - 1 - ); diff --git a/etc/schema/mysql.sql b/etc/schema/mysql.sql new file mode 100644 index 0000000000..fcf27805b3 --- /dev/null +++ b/etc/schema/mysql.sql @@ -0,0 +1,14 @@ +create table account ( + `username` varchar(255) COLLATE latin1_general_ci NOT NULL, + `salt` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `active` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +create table `preference`( + `username` VARCHAR(255) COLLATE latin1_general_ci NOT NULL, + `key` VARCHAR(100) COLLATE latin1_general_ci NOT NULL, + `value` VARCHAR(255) NOT NULL, + PRIMARY KEY (`username`, `key`) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/etc/schema/accounts.pgsql.sql b/etc/schema/pgsql.sql similarity index 50% rename from etc/schema/accounts.pgsql.sql rename to etc/schema/pgsql.sql index 84ecb54e83..f83517d305 100644 --- a/etc/schema/accounts.pgsql.sql +++ b/etc/schema/pgsql.sql @@ -10,19 +10,13 @@ ALTER TABLE ONLY "account" CREATE UNIQUE INDEX username_lower_unique_idx ON "account" USING btree (lower((username)::text)); -/* - * user: icingaadmin - * password: icinga - */ -INSERT INTO "account" ( - "username", - "salt", - "password", - "active" - ) - VALUES ( - 'icingaadmin', - '57cfd5746224be4f60c25d4e8514bec35ad2d01810723a138756b285898e71b2', - '43f8e0588eb39f1a41383b48def0b1fdc45e79b8f67194cccee4453eb3f4ea13', - true - ); +create table "preference"( + "username" VARCHAR(255) NOT NULL, + "key" VARCHAR(100) NOT NULL, + "value" VARCHAR(255) NOT NULL +); + +ALTER TABLE ONLY "preference" + ADD CONSTRAINT preference_pkey PRIMARY KEY ("username", "key"); + +CREATE UNIQUE INDEX username_and_key_lower_unique_idx ON "preference" USING btree (lower((username)::text), lower((key)::text)); \ No newline at end of file diff --git a/etc/schema/preferences.mysql.sql b/etc/schema/preferences.mysql.sql deleted file mode 100644 index 5549daa3fc..0000000000 --- a/etc/schema/preferences.mysql.sql +++ /dev/null @@ -1,6 +0,0 @@ -create table `preference`( - `username` VARCHAR(255) COLLATE latin1_general_ci NOT NULL, - `key` VARCHAR(100) COLLATE latin1_general_ci NOT NULL, - `value` VARCHAR(255) NOT NULL, - PRIMARY KEY (`username`, `key`) -) ENGINE=InnoDB; \ No newline at end of file diff --git a/etc/schema/preferences.pgsql.sql b/etc/schema/preferences.pgsql.sql deleted file mode 100644 index 8b694f0a03..0000000000 --- a/etc/schema/preferences.pgsql.sql +++ /dev/null @@ -1,10 +0,0 @@ -create table "preference"( - "username" VARCHAR(255) NOT NULL, - "key" VARCHAR(100) NOT NULL, - "value" VARCHAR(255) NOT NULL -); - -ALTER TABLE ONLY "preference" - ADD CONSTRAINT preference_pkey PRIMARY KEY ("username", "key"); - -CREATE UNIQUE INDEX username_and_key_lower_unique_idx ON "preference" USING btree (lower((username)::text), lower((key)::text)); \ No newline at end of file From 627a19a2a93f2253d215084c358f022dc1be4a57 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 7 Oct 2014 15:57:00 +0200 Subject: [PATCH 46/62] Fix warning "references should be passed by variable" --- library/Icinga/Application/Platform.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Application/Platform.php b/library/Icinga/Application/Platform.php index 179e2578c2..d31fce70e0 100644 --- a/library/Icinga/Application/Platform.php +++ b/library/Icinga/Application/Platform.php @@ -126,7 +126,8 @@ protected static function discoverHostname() if (substr(self::$fqdn, 0, strlen(self::$hostname)) === self::$hostname) { self::$domain = substr(self::$fqdn, strlen(self::$hostname) + 1); } else { - self::$domain = array_shift(preg_split('~\.~', self::$hostname, 2)); + $parts = preg_split('~\.~', self::$hostname, 2); + self::$domain = array_shift($parts); } } From a9ae75b6b0168e81801ab8b567edc3e1fc680c90 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 7 Oct 2014 16:05:20 +0200 Subject: [PATCH 47/62] monitoring: Fix that sorting a data view is not possible if its sort rules are empty --- .../library/Monitoring/DataView/DataView.php | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index 661412c438..acd377d03b 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -204,42 +204,43 @@ public function pivot($xAxisColumn, $yAxisColumn) public function sort($column = null, $order = null) { $sortRules = $this->getSortRules(); - - if ($sortRules !== null) { - if ($column === null) { - $sortColumns = reset($sortRules); + if ($column === null) { + // Use first available sort rule as default + if (empty($sortRules)) { + return $this; + } + $sortColumns = reset($sortRules); + if (! isset($sortColumns['columns'])) { + $sortColumns['columns'] = array(key($sortRules)); + } + } else { + if (isset($sortRules[$column])) { + $sortColumns = $sortRules[$column]; if (! isset($sortColumns['columns'])) { - $sortColumns['columns'] = array(key($sortRules)); + $sortColumns['columns'] = array($column); } } else { - if (isset($sortRules[$column])) { - $sortColumns = $sortRules[$column]; - if (! isset($sortColumns['columns'])) { - $sortColumns['columns'] = array($column); - } - } else { - $sortColumns = array( - 'columns' => array($column), - 'order' => $order - ); - }; - } + $sortColumns = array( + 'columns' => array($column), + 'order' => $order + ); + }; + } - $order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : self::SORT_ASC) : $order; - $order = (strtoupper($order) === self::SORT_ASC) ? 'ASC' : 'DESC'; + $order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : self::SORT_ASC) : $order; + $order = (strtoupper($order) === self::SORT_ASC) ? 'ASC' : 'DESC'; - foreach ($sortColumns['columns'] as $column) { - if (! $this->isValidFilterTarget($column)) { - throw new QueryException( - t('The sort column "%s" is not allowed in "%s".'), - $column, - get_class($this) - ); - } - $this->query->order($column, $order); + foreach ($sortColumns['columns'] as $column) { + if (! $this->isValidFilterTarget($column)) { + throw new QueryException( + t('The sort column "%s" is not allowed in "%s".'), + $column, + get_class($this) + ); } - $this->isSorted = true; + $this->query->order($column, $order); } + $this->isSorted = true; return $this; } @@ -250,7 +251,7 @@ public function sort($column = null, $order = null) */ public function getSortRules() { - return null; + return array(); } /** @@ -365,6 +366,9 @@ public function addFilter(Filter $filter) */ public function paginate($itemsPerPage = null, $pageNumber = null) { + if (! $this->isSorted) { + $this->order(); + } return $this->query->paginate($itemsPerPage, $pageNumber); } From fcc56c98099c69d46ffeac81b66a590fe2726865 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Tue, 7 Oct 2014 16:09:19 +0200 Subject: [PATCH 48/62] monitoring: Fix some of `DataView's PHPDoc --- .../library/Monitoring/DataView/DataView.php | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index acd377d03b..1262e13e21 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -7,7 +7,6 @@ use Countable; use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\FilterMatch; -use Icinga\Data\SimpleQuery; use Icinga\Data\Browsable; use Icinga\Data\PivotTable; use Icinga\Data\Sortable; @@ -26,7 +25,7 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable /** * The query used to populate the view * - * @var SimpleQuery + * @var \Icinga\Data\SimpleQuery */ private $query; @@ -39,8 +38,8 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable /** * Create a new view * - * @param SimpleQuery $query Which backend to query - * @param array $columns Select columns + * @param ConnectionInterface $connection + * @param array $columns */ public function __construct(ConnectionInterface $connection, array $columns = null) { @@ -64,17 +63,17 @@ public static function getQueryName() return $tableName; } -public function where($condition, $value = null) -{ - $this->filter->addFilter(Filter::where($condition, $value)); - $this->query->where($condition, $value); - return $this; -} + public function where($condition, $value = null) + { + $this->filter->addFilter(Filter::where($condition, $value)); + $this->query->where($condition, $value); + return $this; + } -public function dump() -{ - return $this->query->dump(); -} + public function dump() + { + return $this->query->dump(); + } /** * Retrieve columns provided by this view @@ -194,9 +193,10 @@ public function pivot($xAxisColumn, $yAxisColumn) * Sort the rows, according to the specified sort column and order * * @param string $column Sort column - * @param int $order Sort order, one of the SORT_ constants + * @param string $order Sort order, one of the SORT_ constants * - * @return self + * @return $this + * @throws QueryException If the sort column is not allowed * @see DataView::SORT_ASC * @see DataView::SORT_DESC * @deprecated Use DataView::order() instead @@ -227,8 +227,8 @@ public function sort($column = null, $order = null) }; } - $order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : self::SORT_ASC) : $order; - $order = (strtoupper($order) === self::SORT_ASC) ? 'ASC' : 'DESC'; + $order = $order === null ? (isset($sortColumns['order']) ? $sortColumns['order'] : static::SORT_ASC) : $order; + $order = (strtoupper($order) === static::SORT_ASC) ? 'ASC' : 'DESC'; foreach ($sortColumns['columns'] as $column) { if (! $this->isValidFilterTarget($column)) { @@ -260,7 +260,7 @@ public function getSortRules() * @param string $column * @param string $direction * - * @return self + * @return $this */ public function order($column = null, $direction = null) { @@ -295,7 +295,7 @@ public function getMappedField($field) /** * Return the query which was created in the constructor * - * @return mixed + * @return \Icinga\Data\SimpleQuery */ public function getQuery() { From 1136a097411f320822e73178f488193eccb17f10 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 7 Oct 2014 17:05:55 +0200 Subject: [PATCH 49/62] Wrap report message registration in a specific method refs #7163 --- library/Icinga/Application/WebInstaller.php | 49 ++++++++++----------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index 1ee4653a0e..836bd1d2dc 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -52,46 +52,28 @@ public function run() $configIniPath = Config::resolvePath('config.ini'); try { $this->writeConfigIni($configIniPath); - $this->report[] = (object) array( - 'state' => true, - 'message' => sprintf(t('Successfully created: %s'), $configIniPath) - ); + $this->log(sprintf(t('Successfully created: %s'), $configIniPath)); } catch (Exception $e) { $success = false; - $this->report[] = (object) array( - 'state' => false, - 'message' => sprintf(t('Unable to create: %s (%s)'), $configIniPath, $e->getMessage()) - ); + $this->log(sprintf(t('Unable to create: %s (%s)'), $configIniPath, $e->getMessage()), false); } $resourcesIniPath = Config::resolvePath('resources.ini'); try { $this->writeResourcesIni($resourcesIniPath); - $this->report[] = (object) array( - 'state' => true, - 'message' => sprintf(t('Successfully created: %s'), $resourcesIniPath) - ); + $this->log(sprintf(t('Successfully created: %s'), $resourcesIniPath)); } catch (Exception $e) { $success = false; - $this->report[] = (object) array( - 'state' => false, - 'message' => sprintf(t('Unable to create: %s (%s)'), $resourcesIniPath, $e->getMessage()) - ); + $this->log(sprintf(t('Unable to create: %s (%s)'), $resourcesIniPath, $e->getMessage()), false); } $authenticationIniPath = Config::resolvePath('authentication.ini'); try { $this->writeAuthenticationIni($authenticationIniPath); - $this->report[] = (object) array( - 'state' => true, - 'message' => sprintf(t('Successfully created: %s'), $authenticationIniPath) - ); + $this->log(sprintf(t('Successfully created: %s'), $authenticationIniPath)); } catch (Exception $e) { $success = false; - $this->report[] = (object) array( - 'state' => false, - 'message' => sprintf(t('Unable to create: %s (%s)'), $authenticationIniPath, $e->getMessage()) - ); + $this->log(sprintf(t('Unable to create: %s (%s)'), $authenticationIniPath, $e->getMessage()), false); } return $success; @@ -365,7 +347,10 @@ public function getSummary() try { $db->connectToDb(); $message = sprintf( - t('The database user "%s" will be used to setup the missing schema required by Icinga Web 2 in database "%s".'), + t( + 'The database user "%s" will be used to setup the missing' + . ' schema required by Icinga Web 2 in database "%s".' + ), $this->pageData['setup_database_creation']['username'], $resourceConfig['dbname'] ); @@ -394,4 +379,18 @@ public function getReport() { return $this->report; } + + /** + * Add a message to the report + * + * @param string $message The message to add + * @param bool $success Whether the message represents a success (true) or a failure (false) + */ + protected function log($message, $success = true) + { + $this->report[] = (object) array( + 'state' => (bool) $success, + 'message' => $message + ); + } } From 7da11b83f53f4079c220f054b29b5e1e8227d0bd Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 7 Oct 2014 17:06:51 +0200 Subject: [PATCH 50/62] Fix that the database is displayed instead of the login in the db summary refs #7163 --- library/Icinga/Application/WebInstaller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index 836bd1d2dc..736e74cd90 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -362,7 +362,7 @@ public function getSummary() ), $this->pageData['setup_database_creation']['username'], $resourceConfig['dbname'], - $this->pageData['setup_db_resource']['dbname'] + $this->pageData['setup_db_resource']['username'] ); } From abc34a5f1a6f24bd8b7cba227f95e6d0ba1b4a2d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 7 Oct 2014 17:08:50 +0200 Subject: [PATCH 51/62] Add database setup routines refs #7163 --- library/Icinga/Application/WebInstaller.php | 165 ++++++++++++++++++++ library/Icinga/Web/Setup/DbTool.php | 66 ++++++++ 2 files changed, 231 insertions(+) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index 736e74cd90..e09b58d551 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -8,6 +8,7 @@ use Zend_Config; use PDOException; use Icinga\Web\Setup\DbTool; +use Icinga\Application\Icinga; use Icinga\Application\Config; use Icinga\Web\Setup\Installer; use Icinga\Config\PreservingIniWriter; @@ -49,6 +50,17 @@ public function run() { $success = true; + if (isset($this->pageData['setup_db_resource']) && ! $this->pageData['setup_db_resource']['skip_validation']) { + try { + $this->setupDatabase(); + } catch (Exception $e) { + $this->log(sprintf(t('Failed to set up the database: %s'), $e->getMessage()), false); + return false; // Bail out as early as possible as not being able to setup the database is fatal + } + + $this->log(t('The database has been successfully set up!')); + } + $configIniPath = Config::resolvePath('config.ini'); try { $this->writeConfigIni($configIniPath); @@ -189,6 +201,159 @@ protected function writeAuthenticationIni($configPath) $writer->write(); } + /** + * Setup the database + */ + protected function setupDatabase() + { + $resourceConfig = $this->pageData['setup_db_resource']; + if (isset($this->pageData['setup_database_creation'])) { + $resourceConfig['username'] = $this->pageData['setup_database_creation']['username']; + $resourceConfig['password'] = $this->pageData['setup_database_creation']['password']; + } + + $db = new DbTool($resourceConfig); + if ($resourceConfig['db'] === 'mysql') { + $this->setupMysqlDatabase($db); + } elseif ($resourceConfig['db'] === 'pgsql') { + $this->setupPgsqlDatabase($db); + } + } + + /** + * Setup a MySQL database + * + * @param DbTool $db The database connection wrapper to use + * + * @todo Escape user input or make use of prepared statements! + */ + private function setupMysqlDatabase(DbTool $db) + { + try { + $db->connectToDb(); + $this->log(sprintf( + t('Successfully connected to existing database "%s"...'), + $this->pageData['setup_db_resource']['dbname'] + )); + } catch (PDOException $e) { + $db->connectToHost(); + $this->log(sprintf( + t('Creating new database "%s"...'), + $this->pageData['setup_db_resource']['dbname'] + )); + $db->exec('CREATE DATABASE ' . $this->pageData['setup_db_resource']['dbname']); + $db->reconnect($this->pageData['setup_db_resource']['dbname']); + } + + $loginIdent = "'" . $this->pageData['setup_db_resource']['username'] . "'@'" . Platform::getFqdn() . "'"; + if (false === array_search($loginIdent, $db->listLogins())) { + $this->log(sprintf( + t('Creating login "%s"...'), + $this->pageData['setup_db_resource']['username'] + )); + $db->exec( + "CREATE USER $loginIdent IDENTIFIED BY '" . + $this->pageData['setup_db_resource']['password'] . "'" + ); + } else { + $this->log(sprintf( + t('Login "%s" already exists...'), + $this->pageData['setup_db_resource']['username'] + )); + } + + $diff = array_diff(array('account', 'preference'), $db->listTables()); + if (empty($diff)) { + $this->log(t('Database schema already exists...')); + } else { + $this->log(t('Creating database schema...')); + $db->import(Icinga::app()->getApplicationDir() . '/../etc/schema/mysql.sql'); + } + + $privileges = array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'EXECUTE', 'CREATE TEMPORARY TABLES'); + if ($db->checkPrivileges(array_merge($privileges, array('GRANT OPTION')))) { + $this->log(sprintf( + t('Granting required privileges to login "%s"...'), + $this->pageData['setup_db_resource']['username'] + )); + $db->exec(sprintf( + "GRANT %s ON %s.* TO %s", + join(',', $privileges), + $this->pageData['setup_db_resource']['dbname'], + $loginIdent + )); + } + } + + /** + * Setup a PostgreSQL database + * + * @param DbTool $db The database connection wrapper to use + * + * @todo Escape user input or make use of prepared statements! + */ + private function setupPgsqlDatabase(DbTool $db) + { + try { + $db->connectToDb(); + $this->log(sprintf( + t('Successfully connected to existing database "%s"...'), + $this->pageData['setup_db_resource']['dbname'] + )); + } catch (PDOException $e) { + $db->connectToHost(); + $this->log(sprintf( + t('Creating new database "%s"...'), + $this->pageData['setup_db_resource']['dbname'] + )); + $db->exec('CREATE DATABASE ' . $this->pageData['setup_db_resource']['dbname']); + $db->reconnect($this->pageData['setup_db_resource']['dbname']); + } + + if (false === array_search($this->pageData['setup_db_resource']['username'], $db->listLogins())) { + $this->log(sprintf( + t('Creating login "%s"...'), + $this->pageData['setup_db_resource']['username'] + )); + $db->exec(sprintf( + "CREATE USER %s WITH PASSWORD '%s'", + $this->pageData['setup_db_resource']['username'], + $this->pageData['setup_db_resource']['password'] + )); + } else { + $this->log(sprintf( + t('Login "%s" already exists...'), + $this->pageData['setup_db_resource']['username'] + )); + } + + $diff = array_diff(array('account', 'preference'), $db->listTables()); + if (empty($diff)) { + $this->log(t('Database schema already exists...')); + } else { + $this->log(t('Creating database schema...')); + $db->import(Icinga::app()->getApplicationDir() . '/../etc/schema/pgsql.sql'); + } + + $privileges = array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); + if ($db->checkPrivileges(array_merge($privileges, array('GRANT OPTION')))) { + $this->log(sprintf( + t('Granting required privileges to login "%s"...'), + $this->pageData['setup_db_resource']['username'] + )); + $db->exec(sprintf( + "GRANT %s ON TABLE account TO %s", + join(',', $privileges), + $this->pageData['setup_db_resource']['username'] + )); + $db->exec(sprintf( + "GRANT %s ON TABLE preference TO %s", + join(',', $privileges), + $this->pageData['setup_db_resource']['username'] + )); + } + } + /** * @see Installer::getSummary() */ diff --git a/library/Icinga/Web/Setup/DbTool.php b/library/Icinga/Web/Setup/DbTool.php index a3ded97074..982c6bc673 100644 --- a/library/Icinga/Web/Setup/DbTool.php +++ b/library/Icinga/Web/Setup/DbTool.php @@ -9,6 +9,7 @@ use LogicException; use Zend_Db_Adapter_Pdo_Mysql; use Zend_Db_Adapter_Pdo_Pgsql; +use Icinga\Util\File; use Icinga\Exception\ConfigurationError; /** @@ -129,6 +130,18 @@ public function connect($dbname = null) } } + /** + * Reestablish a connection with the database or just the server by omitting the database name + * + * @param string $dbname The name of the database to connect to + */ + public function reconnect($dbname = null) + { + $this->pdoConn = null; + $this->zendConn = null; + $this->connect($dbname); + } + /** * Initialize Zend database adapter * @@ -228,6 +241,35 @@ public function checkConnectivity() } } + /** + * Execute a SQL statement and return the affected row count + * + * @param string $statement The statement to execute + * + * @return int + */ + public function exec($statement) + { + return $this->pdoConn->exec($statement); + } + + /** + * Import the given SQL file + * + * @param string $filepath The file to import + */ + public function import($filepath) + { + $file = new File($filepath); + $content = join(PHP_EOL, iterator_to_array($file)); // There is no fread() before PHP 5.5 :( + + foreach (explode(';', $content) as $statement) { + if (($statement = trim($statement)) !== '') { + $this->exec($statement); + } + } + } + /** * Return whether the given privileges were granted * @@ -250,4 +292,28 @@ public function listTables() $this->assertConnectedToDb(); return $this->zendConn->listTables(); } + + /** + * Return a list of all available database logins + * + * @return array + */ + public function listLogins() + { + $users = array(); + + if ($this->config['db'] === 'mysql') { + $query = $this->pdoConn->query('SELECT DISTINCT grantee FROM information_schema.user_privileges'); + foreach ($query->fetchAll() as $row) { + $users[] = $row['grantee']; + } + } elseif ($this->config['db'] === 'pgsql') { + $query = $this->pdoConn->query('SELECT usename FROM pg_catalog.pg_user'); + foreach ($query->fetchAll() as $row) { + $users[] = $row['usename']; + } + } + + return $users; + } } From 8f787e649c3e9949eaf001ce739cecc0d02d8eed Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 08:52:04 +0200 Subject: [PATCH 52/62] Update documentation and fix vagrant manifest due to moved schema files --- .vagrant-puppet/manifests/default.pp | 20 ++++---------------- doc/installation.md | 6 ++---- packages/rpm/README.md | 6 ++---- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/.vagrant-puppet/manifests/default.pp b/.vagrant-puppet/manifests/default.pp index 39b0d967cc..117ad0836f 100644 --- a/.vagrant-puppet/manifests/default.pp +++ b/.vagrant-puppet/manifests/default.pp @@ -601,27 +601,15 @@ require => Service['postgresql'] } -exec { 'populate-icingaweb-mysql-db-accounts': +exec { 'populate-icingaweb-mysql-db-tables': unless => 'mysql -uicingaweb -picingaweb icingaweb -e "SELECT * FROM account;" &> /dev/null', - command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/accounts.mysql.sql', + command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/mysql.sql', require => [ Exec['create-mysql-icingaweb-db'] ] } -exec { 'populate-icingweba-pgsql-db-accounts': +exec { 'populate-icingweba-pgsql-db-tables': unless => 'psql -U icingaweb -d icingaweb -c "SELECT * FROM account;" &> /dev/null', - command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/accounts.pgsql.sql', - require => [ Exec['create-pgsql-icingaweb-db'] ] -} - -exec { 'populate-icingaweb-mysql-db-preferences': - unless => 'mysql -uicingaweb -picingaweb icingaweb -e "SELECT * FROM preference;" &> /dev/null', - command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/preferences.mysql.sql', - require => [ Exec['create-mysql-icingaweb-db'] ] -} - -exec { 'populate-icingweba-pgsql-db-preferences': - unless => 'psql -U icingaweb -d icingaweb -c "SELECT * FROM preference;" &> /dev/null', - command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/preferences.pgsql.sql', + command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/pgsql.sql', require => [ Exec['create-pgsql-icingaweb-db'] ] } diff --git a/doc/installation.md b/doc/installation.md index 9de3f7d5ce..60a5a3ce58 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -74,8 +74,7 @@ create all database tables. You will find the installation guides for the differ > > RPM packages install the schema into /usr/share/doc/icingaweb-<version>/schema - bash$ mysql -u root -p icingaweb < etc/schema/accounts.mysql.sql - bash$ mysql -u root -p icingaweb < etc/schema/preferences.mysql.sql + bash$ mysql -u root -p icingaweb < etc/schema/mysql.sql #### PostgreSQL @@ -108,8 +107,7 @@ And restart your database ('service postgresql restart' or '/etc/init.d/postgres > > RPM packages install the schema into /usr/share/doc/icingaweb-<version>/schema - bash$ psql -U icingaweb -a -f etc/schema/accounts.pgsql.sql - bash$ psql -U icingaweb -a -f etc/schema/preferences.pgsql.sql + bash$ psql -U icingaweb -a -f etc/schema/pgsql.sql diff --git a/packages/rpm/README.md b/packages/rpm/README.md index 5dd1006c5e..293682a76c 100644 --- a/packages/rpm/README.md +++ b/packages/rpm/README.md @@ -42,8 +42,7 @@ Decide whether to use MySQL or PostgreSQL. FLUSH PRIVILEGES; quit - mysql -u root -p icingaweb < /usr/share/doc/icingaweb2*/schema/accounts.mysql.sql - mysql -u root -p icingaweb < /usr/share/doc/icingaweb2*/schema/preferences.mysql.sql + mysql -u root -p icingaweb < /usr/share/doc/icingaweb2*/schema/mysql.sql ### PostgreSQL @@ -62,8 +61,7 @@ in `/var/lib/pgsql/data/pg_hba.conf` and restart the PostgreSQL server. Now install the `icingaweb` schema - bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2*/schema/accounts.pgsql.sql - bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2*/schema/preferences.pgsql.sql + bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2*/schema/pgsql.sql ## Configuration From 393191ced139267fe56829f80a97c5c4636cfc38 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 10:26:12 +0200 Subject: [PATCH 53/62] Add admin creation routine refs #7163 --- library/Icinga/Application/WebInstaller.php | 29 ++++++++++++++ .../Authentication/Backend/DbUserBackend.php | 40 +++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index e09b58d551..54323f9663 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -11,7 +11,9 @@ use Icinga\Application\Icinga; use Icinga\Application\Config; use Icinga\Web\Setup\Installer; +use Icinga\Data\ResourceFactory; use Icinga\Config\PreservingIniWriter; +use Icinga\Authentication\Backend\DbUserBackend; /** * Icinga Web 2 Installer @@ -88,6 +90,14 @@ public function run() $this->log(sprintf(t('Unable to create: %s (%s)'), $authenticationIniPath, $e->getMessage()), false); } + try { + $this->setupAdminAccount(); + $this->log(t('Successfully created initial administrative account.')); + } catch (Exception $e) { + $success = false; + $this->log(sprintf(t('Failed to create initial administrative account: %s'), $e->getMessage())); + } + return $success; } @@ -354,6 +364,25 @@ private function setupPgsqlDatabase(DbTool $db) } } + /** + * Create the initial administrative account + */ + protected function setupAdminAccount() + { + if ($this->pageData['setup_admin_account']['user_type'] === 'new_user') { + $backend = new DbUserBackend( + ResourceFactory::createResource(new Zend_Config($this->pageData['setup_db_resource'])) + ); + + if (array_search($this->pageData['setup_admin_account']['new_user'], $backend->listUsers()) === false) { + $backend->addUser( + $this->pageData['setup_admin_account']['new_user'], + $this->pageData['setup_admin_account']['new_user_password'] + ); + } + } + } + /** * @see Installer::getSummary() */ diff --git a/library/Icinga/Authentication/Backend/DbUserBackend.php b/library/Icinga/Authentication/Backend/DbUserBackend.php index 239ceb2581..9c45c56937 100644 --- a/library/Icinga/Authentication/Backend/DbUserBackend.php +++ b/library/Icinga/Authentication/Backend/DbUserBackend.php @@ -20,7 +20,7 @@ class DbUserBackend extends UserBackend * * @var DbConnection */ - private $conn; + protected $conn; public function __construct(DbConnection $conn) { @@ -44,6 +44,28 @@ public function hasUser(User $user) return ($row !== false) ? true : false; } + /** + * Add a new user + * + * @param string $username The name of the new user + * @param string $password The new user's password + * @param bool $active Whether the user is active + */ + public function addUser($username, $password, $active = true) + { + $passwordSalt = $this->generateSalt(); + $hashedPassword = $this->hashPassword($password, $passwordSalt); + $stmt = $this->conn->getDbAdapter()->prepare( + 'INSERT INTO account VALUES (:username, :salt, :password, :active);' + ); + $stmt->execute(array( + ':active' => $active, + ':username' => $username, + ':salt' => $passwordSalt, + ':password' => $hashedPassword + )); + } + /** * Authenticate the given user and return true on success, false on failure and null on error * @@ -92,13 +114,25 @@ public function authenticate(User $user, $password) * * @return string|null */ - private function getSalt($username) + protected function getSalt($username) { $select = new Zend_Db_Select($this->conn->getConnection()); $row = $select->from('account', array('salt'))->where('username = ?', $username)->query()->fetchObject(); return ($row !== false) ? $row->salt : null; } + /** + * Return a random salt + * + * The returned salt is safe to be used for hashing a user's password + * + * @return string + */ + protected function generateSalt() + { + return openssl_random_pseudo_bytes(64); + } + /** * Hash a password * @@ -107,7 +141,7 @@ private function getSalt($username) * * @return string */ - private function hashPassword($password, $salt) { + protected function hashPassword($password, $salt) { return hash_hmac('sha256', $password, $salt); } From 647447b4d2a12ecefcefe4dedcb630d41a8d67e0 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 10:38:04 +0200 Subject: [PATCH 54/62] Ensure that a new admin account does not already exist refs #7163 --- application/forms/Setup/AdminAccountPage.php | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/application/forms/Setup/AdminAccountPage.php b/application/forms/Setup/AdminAccountPage.php index 79237bac1c..fc3a18fa8e 100644 --- a/application/forms/Setup/AdminAccountPage.php +++ b/application/forms/Setup/AdminAccountPage.php @@ -187,6 +187,27 @@ public function createElements(array $formData) ); } + /** + * Validate the given request data and ensure that any new user does not already exist + * + * @param array $data The request data to validate + * + * @return bool + */ + public function isValid($data) + { + if (false === parent::isValid($data)) { + return false; + } + + if ($data['user_type'] === 'new_user' && array_search($data['new_user'], $this->fetchUsers()) !== false) { + $this->getElement('new_user')->addError(t('Username already exists.')); + return false; + } + + return true; + } + /** * Return the name of the externally authenticated user * From baa6abdb007f40736d12cdff2439de136eb7214a Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 11:24:05 +0200 Subject: [PATCH 55/62] Relax schema check Checking one table for its existence is more than enough. Creating a diff implies conditional CREATE TABLE instructions and the like, what we want to avoid. refs #7163 --- library/Icinga/Application/WebInstaller.php | 6 ++---- library/Icinga/Application/WebSetup.php | 10 +--------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index 54323f9663..1e225f94e3 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -272,8 +272,7 @@ private function setupMysqlDatabase(DbTool $db) )); } - $diff = array_diff(array('account', 'preference'), $db->listTables()); - if (empty($diff)) { + if (array_search('account', $db->listTables()) !== false) { $this->log(t('Database schema already exists...')); } else { $this->log(t('Creating database schema...')); @@ -337,8 +336,7 @@ private function setupPgsqlDatabase(DbTool $db) )); } - $diff = array_diff(array('account', 'preference'), $db->listTables()); - if (empty($diff)) { + if (array_search('account', $db->listTables()) !== false) { $this->log(t('Database schema already exists...')); } else { $this->log(t('Creating database schema...')); diff --git a/library/Icinga/Application/WebSetup.php b/library/Icinga/Application/WebSetup.php index 9f0fa04b66..84f732789d 100644 --- a/library/Icinga/Application/WebSetup.php +++ b/library/Icinga/Application/WebSetup.php @@ -29,13 +29,6 @@ */ class WebSetup extends Wizard implements SetupWizard { - /** - * The database tables required by Icinga Web 2 - * - * @var array - */ - protected $databaseTables = array('account', 'preference'); - /** * The privileges required by Icinga Web 2 to setup the database * @@ -148,8 +141,7 @@ protected function getNewPage($requestedPage, Form $originPage) try { $db->connectToDb(); - $diff = array_diff($this->databaseTables, $db->listTables()); - if (false === empty($diff)) { + if (array_search('account', $db->listTables()) === false) { $skip = $db->checkPrivileges($this->databaseSetupPrivileges); } else { $skip = true; From afc97b1cac327f357d2f597d0bf40322b1b7aea2 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Wed, 8 Oct 2014 12:30:08 +0200 Subject: [PATCH 56/62] Fix the duplicate entries in monitoring list shows refs #7057 refs #7344 fixes #7057 --- library/Icinga/Data/Db/DbQuery.php | 7 +-- .../Backend/Ido/Query/GroupsummaryQuery.php | 2 +- .../Backend/Ido/Query/StatusQuery.php | 43 ++++++++++++++++++- .../library/Monitoring/DataView/DataView.php | 14 +++++- .../Monitoring/DataView/HostStatus.php | 8 ++++ .../Monitoring/DataView/ServiceStatus.php | 10 +++++ 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index c50049ee72..94765d98ff 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -96,14 +96,14 @@ protected function dbSelect() public function getSelectQuery() { $select = $this->dbSelect(); - // Add order fields to select for postgres distinct queries (#6351) if ($this->hasOrder() && $this->getDatasource()->getDbType() === 'pgsql' && $select->getPart(Zend_Db_Select::DISTINCT) === true) { foreach ($this->getOrder() as $fieldAndDirection) { - list($alias, $field) = explode('.', $fieldAndDirection[0]); - $this->columns[$field] = $fieldAndDirection[0]; + if (array_search($fieldAndDirection[0], $this->columns) === false) { + $this->columns[] = $fieldAndDirection[0]; + } } } @@ -264,6 +264,7 @@ public function getCountQuery() $this->applyFilterSql($count); if ($this->useSubqueryCount) { + $count->columns($this->columns); $columns = array('cnt' => 'COUNT(*)'); return $this->db->select()->from($count, $columns); } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php index ec79ef63c0..ab1947f7ec 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/GroupsummaryQuery.php @@ -79,7 +79,7 @@ protected function joinBaseTables() } $union = $this->db->select()->union(array($hosts, $services), Zend_Db_Select::SQL_UNION_ALL); - $this->select->from(array('statussummary' => $union), array($groupColumn))->group(array($groupColumn)); + $this->select->from(array('statussummary' => $union), array())->group(array($groupColumn)); $this->joinedVirtualTables = array( 'servicestatussummary' => true, diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php index d3aca9934d..e390aa114d 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php @@ -8,6 +8,29 @@ class StatusQuery extends IdoQuery { + /** + * This mode represents whether we are in HostStatus or ServiceStatus + * + * Implemented for `distinct as workaround + * + * @TODO Subject to change, see #7344 + * + * @var string + */ + protected $mode; + + /** + * Sets the mode of the current query + * + * @TODO Subject to change, see #7344 + * + * @param string $mode + */ + public function setMode($mode) + { + $this->mode = $mode; + } + protected $allowCustomVars = true; protected $columnMap = array( @@ -430,6 +453,12 @@ protected function joinHostHostgroups() array() ); + // @TODO Subject to change, see #7344 + if ($this->mode === 'host' || $this->mode === 'service') { + $this->useSubqueryCount = true; + $this->distinct(); + } + return $this; } @@ -449,7 +478,11 @@ protected function joinServiceHostgroups() . ' AND hgo.is_active = 1', array() ); - + // @TODO Subject to change, see #7344 + if ($this->mode === 'service') { + $this->distinct(); + $this->useSubqueryCount = true; + } return $this; } @@ -471,6 +504,14 @@ protected function joinServicegroups() array() ); + // @TODO Subject to change, see #7344 + if ($this->mode === 'host' || $this->mode === 'service') { + $this->distinct(); + } + if ($this->mode === 'host') { + $this->useSubqueryCount = true; + } + return $this; } diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index 1262e13e21..df331999e5 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -27,7 +27,7 @@ abstract class DataView implements Browsable, Countable, Filterable, Sortable * * @var \Icinga\Data\SimpleQuery */ - private $query; + protected $query; protected $filter; @@ -47,6 +47,18 @@ public function __construct(ConnectionInterface $connection, array $columns = nu $queryClass = $connection->getQueryClass($this->getQueryName()); $this->query = new $queryClass($this->connection->getResource(), $columns); $this->filter = Filter::matchAll(); + $this->init(); + } + + /** + * Initializer for `distinct purposes + * + * Implemented for `distinct as workaround + * + * @TODO Subject to change, see #7344 + */ + public function init() + { } /** diff --git a/modules/monitoring/library/Monitoring/DataView/HostStatus.php b/modules/monitoring/library/Monitoring/DataView/HostStatus.php index ff2ecaf913..e9b5b60ddf 100644 --- a/modules/monitoring/library/Monitoring/DataView/HostStatus.php +++ b/modules/monitoring/library/Monitoring/DataView/HostStatus.php @@ -6,6 +6,14 @@ class HostStatus extends DataView { + /** + * @see DataView::init() + */ + public function init() + { + $this->query->setMode('host'); + } + /** * Retrieve columns provided by this view * diff --git a/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php b/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php index 45cde745ff..9edcabc877 100644 --- a/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php +++ b/modules/monitoring/library/Monitoring/DataView/ServiceStatus.php @@ -6,6 +6,16 @@ class ServiceStatus extends DataView { + /** + * Sets the mode for `distinct as workaround + * + * @TODO Subject to change, see #7344 + */ + public function init() + { + $this->query->setMode('service'); + } + /** * Retrieve columns provided by this view * From 96ba45d896130f72a1d0158de069584d4583df74 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 15:26:42 +0200 Subject: [PATCH 57/62] Convert password salt to ASCII to avoid encoding issues with PostgreSQL --- library/Icinga/Authentication/Backend/DbUserBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Authentication/Backend/DbUserBackend.php b/library/Icinga/Authentication/Backend/DbUserBackend.php index 9c45c56937..ead382f2cc 100644 --- a/library/Icinga/Authentication/Backend/DbUserBackend.php +++ b/library/Icinga/Authentication/Backend/DbUserBackend.php @@ -130,7 +130,7 @@ protected function getSalt($username) */ protected function generateSalt() { - return openssl_random_pseudo_bytes(64); + return bin2hex(openssl_random_pseudo_bytes(32)); } /** From 89ae05899b675cf11e7ecb5072ac5eeba3c4740d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 15:33:51 +0200 Subject: [PATCH 58/62] Query a particular login name and create database logins using the DbTool refs #7163 --- library/Icinga/Application/WebInstaller.php | 32 +++++++------- library/Icinga/Web/Setup/DbTool.php | 47 +++++++++++++++------ 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index 1e225f94e3..10a99598ca 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -255,21 +255,20 @@ private function setupMysqlDatabase(DbTool $db) $db->reconnect($this->pageData['setup_db_resource']['dbname']); } - $loginIdent = "'" . $this->pageData['setup_db_resource']['username'] . "'@'" . Platform::getFqdn() . "'"; - if (false === array_search($loginIdent, $db->listLogins())) { + if ($db->hasLogin($this->pageData['setup_db_resource']['username'])) { $this->log(sprintf( - t('Creating login "%s"...'), + t('Login "%s" already exists...'), $this->pageData['setup_db_resource']['username'] )); - $db->exec( - "CREATE USER $loginIdent IDENTIFIED BY '" . - $this->pageData['setup_db_resource']['password'] . "'" - ); } else { $this->log(sprintf( - t('Login "%s" already exists...'), + t('Creating login "%s"...'), $this->pageData['setup_db_resource']['username'] )); + $db->addLogin( + $this->pageData['setup_db_resource']['username'], + $this->pageData['setup_db_resource']['password'] + ); } if (array_search('account', $db->listTables()) !== false) { @@ -289,7 +288,7 @@ private function setupMysqlDatabase(DbTool $db) "GRANT %s ON %s.* TO %s", join(',', $privileges), $this->pageData['setup_db_resource']['dbname'], - $loginIdent + $this->pageData['setup_db_resource']['username'] . '@' . Platform::getFqdn() )); } } @@ -319,21 +318,20 @@ private function setupPgsqlDatabase(DbTool $db) $db->reconnect($this->pageData['setup_db_resource']['dbname']); } - if (false === array_search($this->pageData['setup_db_resource']['username'], $db->listLogins())) { + if ($db->hasLogin($this->pageData['setup_db_resource']['username'])) { $this->log(sprintf( - t('Creating login "%s"...'), + t('Login "%s" already exists...'), $this->pageData['setup_db_resource']['username'] )); - $db->exec(sprintf( - "CREATE USER %s WITH PASSWORD '%s'", - $this->pageData['setup_db_resource']['username'], - $this->pageData['setup_db_resource']['password'] - )); } else { $this->log(sprintf( - t('Login "%s" already exists...'), + t('Creating login "%s"...'), $this->pageData['setup_db_resource']['username'] )); + $db->addLogin( + $this->pageData['setup_db_resource']['username'], + $this->pageData['setup_db_resource']['password'] + ); } if (array_search('account', $db->listTables()) !== false) { diff --git a/library/Icinga/Web/Setup/DbTool.php b/library/Icinga/Web/Setup/DbTool.php index 982c6bc673..157748fa17 100644 --- a/library/Icinga/Web/Setup/DbTool.php +++ b/library/Icinga/Web/Setup/DbTool.php @@ -10,6 +10,7 @@ use Zend_Db_Adapter_Pdo_Mysql; use Zend_Db_Adapter_Pdo_Pgsql; use Icinga\Util\File; +use Icinga\Application\Platform; use Icinga\Exception\ConfigurationError; /** @@ -294,26 +295,44 @@ public function listTables() } /** - * Return a list of all available database logins + * Return whether the given database login exists * - * @return array + * @param string $username The username to search + * + * @return bool */ - public function listLogins() + public function hasLogin($username) { - $users = array(); - if ($this->config['db'] === 'mysql') { - $query = $this->pdoConn->query('SELECT DISTINCT grantee FROM information_schema.user_privileges'); - foreach ($query->fetchAll() as $row) { - $users[] = $row['grantee']; - } + $stmt = $this->pdoConn->prepare( + 'SELECT grantee FROM information_schema.user_privileges WHERE grantee = :ident LIMIT 1' + ); + $stmt->execute(array(':ident' => "'" . $username . "'@'" . Platform::getFqdn() . "'")); + return $stmt->rowCount() === 1; } elseif ($this->config['db'] === 'pgsql') { - $query = $this->pdoConn->query('SELECT usename FROM pg_catalog.pg_user'); - foreach ($query->fetchAll() as $row) { - $users[] = $row['usename']; - } + $stmt = $this->pdoConn->prepare( + 'SELECT usename FROM pg_catalog.pg_user WHERE usename = :ident LIMIT 1' + ); + $stmt->execute(array(':ident' => $username)); + return $stmt->rowCount() === 1; } - return $users; + return false; + } + + /** + * Add a new database login + * + * @param string $username The username of the new login + * @param string $password The password of the new login + */ + public function addLogin($username, $password) + { + if ($this->config['db'] === 'mysql') { + $stmt = $this->pdoConn->prepare('CREATE USER :user@:host IDENTIFIED BY :passw'); + $stmt->execute(array(':user' => $username, ':host' => Platform::getFqdn(), ':passw' => $password)); + } elseif ($this->config['db'] === 'pgsql') { + $this->pdoConn->exec("CREATE USER $username WITH PASSWORD '$password'"); + } } } From 06e82ed89246f70bcf1c492ce3628853f2bb59c7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 15:34:28 +0200 Subject: [PATCH 59/62] Make it possible to skip the db-creation step refs #7163 --- .../forms/Setup/DatabaseCreationPage.php | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/application/forms/Setup/DatabaseCreationPage.php b/application/forms/Setup/DatabaseCreationPage.php index 2c0d70cf84..b8e5adf6cf 100644 --- a/application/forms/Setup/DatabaseCreationPage.php +++ b/application/forms/Setup/DatabaseCreationPage.php @@ -79,11 +79,13 @@ public function createElements(array $formData) ) ) ); + + $skipValidation = isset($formData['skip_validation']) && $formData['skip_validation']; $this->addElement( 'text', 'username', array( - 'required' => true, + 'required' => false === $skipValidation, 'label' => t('Username'), 'description' => t('A user which is able to create databases and/or touch the database schema') ) @@ -92,11 +94,24 @@ public function createElements(array $formData) 'password', 'password', array( - 'required' => true, + 'required' => false === $skipValidation, 'label' => t('Password'), 'description' => t('The password for the database user defined above') ) ); + + if ($skipValidation) { + $this->addSkipValidationCheckbox(); + } else { + $this->addElement( + 'hidden', + 'skip_validation', + array( + 'required' => true, + 'value' => 0 + ) + ); + } } /** @@ -112,6 +127,10 @@ public function isValid($data) return false; } + if (isset($data['skip_validation']) && $data['skip_validation']) { + return true; + } + $this->config['username'] = $this->getValue('username'); $this->config['password'] = $this->getValue('password'); $db = new DbTool($this->config); @@ -122,6 +141,7 @@ public function isValid($data) $this->addError( t('The provided credentials do not have the required access rights to create the database schema.') ); + $this->addSkipValidationCheckbox(); return false; } } catch (PDOException $e) { @@ -131,14 +151,33 @@ public function isValid($data) $this->addError( t('The provided credentials cannot be used to create the database and/or the user.') ); + $this->addSkipValidationCheckbox(); return false; } } catch (PDOException $e) { $this->addError($e->getMessage()); + $this->addSkipValidationCheckbox(); return false; } } return true; } + + /** + * Add a checkbox to the form by which the user can skip the login and privilege validation + */ + protected function addSkipValidationCheckbox() + { + $this->addElement( + 'checkbox', + 'skip_validation', + array( + 'order' => 1, + 'required' => true, + 'label' => t('Skip Validation'), + 'description' => t('Check this to not to validate the ability to login and required privileges') + ) + ); + } } From 8fa8642917904bdbe9dcdd0b7bfb1f9309b7ed21 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 15:35:12 +0200 Subject: [PATCH 60/62] Do not show the db-creation step when the db-resource step was skipped refs #7163 --- library/Icinga/Application/WebSetup.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Application/WebSetup.php b/library/Icinga/Application/WebSetup.php index 84f732789d..a78a61a474 100644 --- a/library/Icinga/Application/WebSetup.php +++ b/library/Icinga/Application/WebSetup.php @@ -22,7 +22,6 @@ use Icinga\Web\Setup\DbTool; use Icinga\Web\Setup\SetupWizard; use Icinga\Web\Setup\Requirements; -use Icinga\Application\Platform; /** * Icinga Web 2 Setup Wizard @@ -136,8 +135,8 @@ protected function getNewPage($requestedPage, Form $originPage) $authData = $this->getPageData('setup_authentication_type'); $skip = $authData['type'] !== 'ldap'; } elseif ($newPage->getName() === 'setup_database_creation') { - if ($this->hasPageData('setup_db_resource')) { - $db = new DbTool($this->getPageData('setup_db_resource')); + if (($config = $this->getPageData('setup_db_resource')) !== null && ! $config['skip_validation']) { + $db = new DbTool($config); try { $db->connectToDb(); From 7c202dd8fa8e2b8cbe77de39563ad5b90f96d5c0 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 15:36:26 +0200 Subject: [PATCH 61/62] Improve the db-summary text and db-creation/-resource step handling refs #7163 --- library/Icinga/Application/WebInstaller.php | 105 ++++++++++++++------ 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/library/Icinga/Application/WebInstaller.php b/library/Icinga/Application/WebInstaller.php index 10a99598ca..14de68fd2d 100644 --- a/library/Icinga/Application/WebInstaller.php +++ b/library/Icinga/Application/WebInstaller.php @@ -8,8 +8,6 @@ use Zend_Config; use PDOException; use Icinga\Web\Setup\DbTool; -use Icinga\Application\Icinga; -use Icinga\Application\Config; use Icinga\Web\Setup\Installer; use Icinga\Data\ResourceFactory; use Icinga\Config\PreservingIniWriter; @@ -52,7 +50,12 @@ public function run() { $success = true; - if (isset($this->pageData['setup_db_resource']) && ! $this->pageData['setup_db_resource']['skip_validation']) { + if (isset($this->pageData['setup_db_resource']) + && ! $this->pageData['setup_db_resource']['skip_validation'] + && (false === isset($this->pageData['setup_database_creation']) + || ! $this->pageData['setup_database_creation']['skip_validation'] + ) + ) { try { $this->setupDatabase(); } catch (Exception $e) { @@ -92,10 +95,10 @@ public function run() try { $this->setupAdminAccount(); - $this->log(t('Successfully created initial administrative account.')); + $this->log(t('Successfully defined initial administrative account.')); } catch (Exception $e) { $success = false; - $this->log(sprintf(t('Failed to create initial administrative account: %s'), $e->getMessage())); + $this->log(sprintf(t('Failed to define initial administrative account: %s'), $e->getMessage()), false); } return $success; @@ -361,11 +364,16 @@ private function setupPgsqlDatabase(DbTool $db) } /** - * Create the initial administrative account + * Define the initial administrative account */ protected function setupAdminAccount() { - if ($this->pageData['setup_admin_account']['user_type'] === 'new_user') { + if ($this->pageData['setup_admin_account']['user_type'] === 'new_user' + && ! $this->pageData['setup_db_resource']['skip_validation'] + && (false === isset($this->pageData['setup_database_creation']) + || ! $this->pageData['setup_database_creation']['skip_validation'] + ) + ) { $backend = new DbUserBackend( ResourceFactory::createResource(new Zend_Config($this->pageData['setup_db_resource'])) ); @@ -528,35 +536,66 @@ public function getSummary() ) ); - if (isset($this->pageData['setup_database_creation'])) { + if (isset($this->pageData['setup_db_resource'])) { + $setupDatabase = true; $resourceConfig = $this->pageData['setup_db_resource']; - $resourceConfig['username'] = $this->pageData['setup_database_creation']['username']; - $resourceConfig['password'] = $this->pageData['setup_database_creation']['password']; - $db = new DbTool($resourceConfig); - - try { - $db->connectToDb(); - $message = sprintf( - t( - 'The database user "%s" will be used to setup the missing' - . ' schema required by Icinga Web 2 in database "%s".' - ), - $this->pageData['setup_database_creation']['username'], - $resourceConfig['dbname'] - ); - } catch (PDOException $e) { - $message = sprintf( - t( - 'The database user "%s" will be used to create the missing database "%s" ' - . 'with the schema required by Icinga Web 2 and a new login called "%s".' - ), - $this->pageData['setup_database_creation']['username'], - $resourceConfig['dbname'], - $this->pageData['setup_db_resource']['username'] - ); + if (isset($this->pageData['setup_database_creation'])) { + $resourceConfig['username'] = $this->pageData['setup_database_creation']['username']; + $resourceConfig['password'] = $this->pageData['setup_database_creation']['password']; } - $summary[t('Database Setup')] = $message; + if ($setupDatabase) { + $db = new DbTool($resourceConfig); + try { + $db->connectToDb(); + if (array_search('account', $db->listTables()) === false) { + $message = sprintf( + t( + 'The database user "%s" will be used to setup the missing' + . ' schema required by Icinga Web 2 in database "%s".' + ), + $resourceConfig['username'], + $resourceConfig['dbname'] + ); + } else { + $message = sprintf( + t('The database "%s" already seems to be fully set up. No action required.'), + $resourceConfig['dbname'] + ); + } + } catch (PDOException $e) { + try { + $db->connectToHost(); + if ($db->hasLogin($this->pageData['setup_db_resource']['username'])) { + $message = sprintf( + t( + 'The database user "%s" will be used to create the missing ' + . 'database "%s" with the schema required by Icinga Web 2.' + ), + $resourceConfig['username'], + $resourceConfig['dbname'] + ); + } else { + $message = sprintf( + t( + 'The database user "%s" will be used to create the missing database "%s" ' + . 'with the schema required by Icinga Web 2 and a new login called "%s".' + ), + $resourceConfig['username'], + $resourceConfig['dbname'], + $this->pageData['setup_db_resource']['username'] + ); + } + } catch (PDOException $e) { + $message = t( + 'No connection to database host possible. You\'ll need to setup the' + . ' database with the schema required by Icinga Web 2 manually.' + ); + } + } + + $summary[t('Database Setup')] = $message; + } } return $summary; From b7d3a47bc61cb6e54864606747c9479809d6b167 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 8 Oct 2014 16:07:43 +0200 Subject: [PATCH 62/62] Add file-owner note to help output of the generateToken command Clarifies that the token file must be owned by the webserver's user as there is no reliable way to determine this user automagically. refs #7163 --- application/clicommands/SetupCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/clicommands/SetupCommand.php b/application/clicommands/SetupCommand.php index f6b6aff123..1028c1843c 100644 --- a/application/clicommands/SetupCommand.php +++ b/application/clicommands/SetupCommand.php @@ -39,7 +39,9 @@ public function showTokenAction() /** * Create a new setup token * - * Re-generates the setup token used to authenticate when installing Icinga Web 2 using the web-based wizard + * Re-generates the setup token used to authenticate when installing Icinga Web 2 using the web-based wizard. + * Note that it is required to run this command while logged in as your webserver's user or to make him the + * owner of the created file afterwards manually. * * USAGE: *