Icingaweb shoul work without optional extensions when they are not used

When optional classes and php extensions like pgsql and additonal Zend-Pdos are
actually required by creating a new resource or authentication backend,
perform a check and display a warning instead of just throwing an exception.

refs #4788
This commit is contained in:
Matthias Jentsch 2014-04-28 16:45:37 +02:00
parent 08260866df
commit 085025ba7d
5 changed files with 537 additions and 14 deletions

View File

@ -30,6 +30,7 @@
namespace Icinga\Form\Config\Authentication;
use \Exception;
use \Icinga\Authentication\Backend\DbUserBackend;
use \Zend_Config;
use Icinga\Data\ResourceFactory;
use Icinga\Authentication\UserBackend;
@ -68,7 +69,6 @@ class DbBackendForm extends BaseBackendForm
{
$this->setName('form_modify_backend');
$name = $this->filterName($this->getBackendName());
$this->addElement(
'text',
'backend_' . $name . '_name',
@ -136,12 +136,12 @@ class DbBackendForm extends BaseBackendForm
public function isValidAuthenticationBackend()
{
try {
$cfg = $this->getConfig();
$backendName = key($cfg);
$testConn = UserBackend::create($backendName, new Zend_Config($cfg[$backendName]));
if ($testConn->count() === 0) {
$this->addErrorMessage(t('No users found under the specified database backend'));
$testConnection = ResourceFactory::createResource(ResourceFactory::getResourceConfig(
$this->getValue('backend_' . $this->filterName($this->getBackendName()) . '_resource')
));
$dbUserBackend = new DbUserBackend($testConnection);
if ($dbUserBackend->count() < 1) {
$this->addErrorMessage(t("No users found under the specified database backend"));
return false;
}
} catch (Exception $e) {

View File

@ -148,7 +148,6 @@ class LdapBackendForm extends BaseBackendForm
'user_class' => $this->getValue($prefix . 'user_class'),
'user_name_attribute' => $this->getValue($prefix . 'user_name_attribute')
);
return array($section => $cfg);
}
@ -161,17 +160,32 @@ class LdapBackendForm extends BaseBackendForm
*/
public function isValidAuthenticationBackend()
{
if (!ResourceFactory::ldapAvailable()) {
/*
* It should be possible to run icingaweb without the php ldap extension, when
* no ldap backends are needed. When the user tries to create an ldap backend
* without ldap installed we need to show him an error.
*/
$this->addErrorMessage(t('Using ldap is not possible, the php extension "ldap" is not installed.'));
return false;
}
try {
$cfg = $this->getConfig();
$backendName = key($cfg);
$testConn = UserBackend::create($backendName, new Zend_Config($cfg[$backendName]));
$backendName = 'backend_' . $this->filterName($this->getBackendName()) . '_name';
$backendConfig = new Zend_Config($cfg[$this->getValue($backendName)]);
$backend = ResourceFactory::createResource(ResourceFactory::getResourceConfig($backendConfig->resource));
$testConn = new LdapUserBackend(
$backend,
$backendConfig->user_class,
$backendConfig->user_name_attribute
);
if ($testConn->count() === 0) {
$this->addErrorMessage(t('No users found on directory server'));
return false;
throw new Exception('No Users Found On Directory Server');
}
} catch (Exception $exc) {
$this->addErrorMessage(sprintf(t('Connection validation failed: %s'), $exc->getMessage()));
$this->addErrorMessage(
t('Connection Validation Failed: ' . $exc->getMessage())
);
return false;
}

View File

@ -0,0 +1,13 @@
<?php
namespace Icinga\Module\Monitoring\Form\Config\Backend;
use Icinga\Module\Monitoring\Form\Config\Backend\EditResourceForm;
class CreateResourceForm extends EditResourceForm
{
public function __construct()
{
}
}

View File

@ -0,0 +1,481 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Resource;
use \Zend_Config;
use Icinga\Web\Form;
use Icinga\Logger\Logger;
use Icinga\Web\Form\Decorator\HelpText;
use Icinga\Data\ResourceFactory;
/**
* Form for modifying a monitoring backend
*/
class EditResourceForm extends Form
{
/**
* The currently edited resource.
*
* @var Zend_Config
*/
private $resource;
/**
* @var string
*/
private $name = '';
/**
* @var string
*/
private $oldName = '';
/**
* Return the current resource name.
*
* @param string $name
*
* @return void|\Zend_Form
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return null|string
*/
public function getName()
{
return $this->getValue('resource_all_name');
}
/**
* Set the original name of the resource. This value is persisted using
* a hidden field.
*
* @param $name
*/
public function setOldName($name)
{
$this->oldName = $name;
}
/**
* Get the resource name that was initially set.
*/
public function getOldName()
{
return $this->getValue('resource_all_name_old');
}
private function addDbForm()
{
$this->addElement(
'select',
'resource_db_db',
array(
'label' => 'Database Type',
'value' => $this->getResource()->get('db', 'mysql'),
'required' => true,
'helptext' => 'The type of SQL database you want to create.',
'multiOptions' => array(
'mysql' => 'MySQL',
'pgsql' => 'PostgreSQL'
//'oracle' => 'Oracle'
)
)
);
$this->addElement(
'text',
'resource_db_host',
array (
'label' => 'Host',
'value' => $this->getResource()->get('host', 'localhost'),
'required' => true,
'helptext' => 'The hostname of the database.'
)
);
$this->addElement(
'text',
'resource_db_port',
array(
'label' => 'Port',
'value' => $this->getResource()->get('port', 3306),
'required' => true,
'validators' => array(
array('regex', false, '/^[0-9]+$/')
),
'helptext' => 'The port number to use.'
)
);
$this->addElement(
'text',
'resource_db_dbname',
array(
'label' => 'Database Name',
'value' => $this->getResource()->get('dbname', ''),
'required' => true,
'helptext' => 'The name of the database to use'
)
);
$this->addElement(
'text',
'resource_db_username',
array (
'label' => 'Username',
'value' => $this->getResource()->get('username', ''),
'required' => true,
'helptext' => 'The user name to use for authentication.'
)
);
$this->addElement(
'password',
'resource_db_password',
array(
'label' => 'Password',
'renderPassword' => true,
'value' => $this->getResource()->get('password', ''),
'helptext' => 'The password to use for authentication',
'required' => true
)
);
}
private function addStatusdatForm()
{
$this->addElement(
'text',
'resource_statusdat_status_file',
array(
'label' => 'Status.dat File',
'value' => $this->getResource()->get('status_file', '/usr/local/icinga/var/status.dat'),
'required' => true,
'helptext' => 'Location of your icinga status.dat file'
)
);
$this->addElement(
'text',
'resource_statusdat_object_file',
array(
'label' => 'Objects.cache File',
'value' => $this->getResource()->get('status_file', '/usr/local/icinga/var/objects.cache'),
'required' => true,
'helptext' => 'Location of your icinga objects.cache file'
)
);
}
private function addLivestatusForm()
{
$this->addElement(
'text',
'resource_livestatus_socket',
array(
'label' => 'Livestatus Socket Location',
'required' => true,
'helptext' => 'The path to your livestatus socket used for querying monitoring data',
'value' => $this->getResource()->socket,
)
);
}
private function addLdapForm()
{
$this->addElement(
'text',
'resource_ldap_hostname',
array(
'label' => 'LDAP Server Host',
'allowEmpty' => false,
'value' => $this->getResource()->get('hostname', 'localhost'),
'helptext' => 'The hostname or address of the LDAP server to use for authentication',
'required' => true
)
);
$this->addElement(
'text',
'resource_ldap_root_dn',
array(
'label' => 'LDAP Root DN',
'value' => $this->getResource()->get('root_dn', 'ou=people,dc=icinga,dc=org'),
'helptext' => 'The path where users can be found on the ldap server',
'required' => true
)
);
$this->addElement(
'text',
'resource_ldap_bind_dn',
array(
'label' => 'LDAP Bind DN',
'value' => $this->getResource()->get('bind_dn', 'cn=admin,cn=config'),
'helptext' => 'The user dn to use for querying the ldap server',
'required' => true
)
);
$this->addElement(
'password',
'resource_ldap_bind_pw',
array(
'label' => 'LDAP Bind Password',
'renderPassword' => true,
'value' => $this->getResource()->get('bind_pw', ''),
'helptext' => 'The password to use for querying the ldap server',
'required' => true
)
);
}
/**
* Set the resource configuration to edit.
*
* @param Zend_Config $resource
*/
public function setResource(Zend_Config $resource)
{
$this->resource = $resource;
}
/**
* Get the current resource configuration.
*
* @return Zend_Config
*/
public function getResource()
{
if (!isset($this->resource)) {
// Init empty resource
$this->resource = new Zend_Config(
array('type' => 'db')
);
}
return $this->resource;
}
/**
* Add a field to change the resource name and one hidden field
* to save the previous resource name.
*/
private function addNameFields()
{
$this->addElement(
'text',
'resource_all_name',
array(
'label' => 'Resource Name',
'value' => $this->name,
'helptext' => 'The unique name of this resource',
'required' => true
)
);
$this->addElement(
'hidden',
'resource_all_name_old',
array(
'value' => $this->oldName
)
);
}
/**
* Add checkbox at the beginning of the form which allows to skip logic connection validation
*/
private function addForceCreationCheckbox()
{
$checkbox = new \Zend_Form_Element_Checkbox(
array(
'name' => 'backend_force_creation',
'label' => 'Force Changes',
'helptext' => 'Check this box to enforce changes without connectivity validation',
'order' => 0
)
);
$checkbox->addDecorator(new HelpText());
$this->addElement($checkbox);
}
/**
* Add a select box for choosing the type to use for this backend
*/
private function addTypeSelectionBox()
{
$this->addElement(
'select',
'resource_type',
array(
'label' => 'Resource Type',
'value' => $this->getResource()->type,
'required' => true,
'helptext' => 'The type of resource.',
'multiOptions' => array(
'db' => 'SQL Database',
'ldap' => 'Ldap',
'statusdat' => 'Status.dat',
'livestatus' => 'Livestatus'
)
)
);
$this->enableAutoSubmit(array('resource_type'));
}
/**
* Validate this form with the Zend validation mechanism and perform a validation of the connection.
*
* If validation fails, the 'backend_force_creation' checkbox is prepended to the form to allow users to
* skip the logic connection validation.
*
* @param array $data The form input to validate
*
* @return bool True when validation succeeded, false if not
*/
public function isValid($data)
{
if (!parent::isValid($data)) {
return false;
}
if ($this->getRequest()->getPost('backend_force_creation')) {
return true;
}
if (!$this->isValidResource()) {
$this->addForceCreationCheckbox();
return false;
}
return true;
}
/**
* Test if the changed resource is a valid resource, by instantiating it and
* checking if connection is possible.
*
* @return bool True when connection to the resource is possible.
*/
private function isValidResource()
{
try {
$config = $this->getConfig();
switch ($config->type) {
case 'db':
/*
* It should be possible to run icingaweb without the pgsql or mysql extension or Zend-Pdo-Classes,
* in case they aren't actually used. When the user tries to create a resource that depends on an
* uninstalled extension, an error should be displayed.
*/
if ($config->db === 'mysql' && !ResourceFactory::mysqlAvailable()) {
$this->addErrorMessage(
'You need to install the php extension "mysql" and the Zend_Pdo_Mysql classes to use '
. ' MySQL database resources.'
);
return false;
}
if ($config->db === 'pgsql' && !ResourceFactory::pgsqlAvailable()) {
$this->addErrorMessage(
'You need to install the php extension "pgsql" and the Zend_Pdo_Pgsql classes to use '
. ' PostgreSQL database resources.'
);
return false;
}
$resource = ResourceFactory::createResource($config);
$resource->getConnection()->getConnection();
break;
case 'statusdat':
if (!file_exists($config->object_file) || !file_exists($config->status_file)) {
$this->addErrorMessage(
'Connectivity validation failed, the provided file or socket does not exist.'
);
return false;
}
break;
case 'livestatus':
// TODO: Implement check
break;
case 'ldap':
$resource = ResourceFactory::createResource($config);
$resource->connect();
break;
}
} catch (\Exception $exc) {
$this->addErrorMessage('Connectivity validation failed, connection to the given resource not possible.');
return false;
}
return true;
}
public function create()
{
$this->addNameFields();
$this->addTypeSelectionBox();
switch ($this->getRequest()->getParam('resource_type', $this->getResource()->type)) {
case 'db':
$this->addDbForm();
break;
case 'statusdat':
$this->addStatusdatForm();
break;
case 'livestatus':
$this->addLivestatusForm();
break;
case 'ldap':
$this->addLdapForm();
break;
}
$this->setSubmitLabel('{{SAVE_ICON}} Save Changes');
}
/**
* Return a configuration containing the backend settings entered in this form
*
* @return Zend_Config The updated configuration for this backend
*/
public function getConfig()
{
$values = $this->getValues();
$type = $values['resource_type'];
$result = array('type' => $type);
foreach ($values as $key => $value) {
if ($key !== 'resource_type' && $key !== 'resource_all_name' && $key !== 'resource_all_name_old') {
$configKey = explode('_', $key, 3);
if (sizeof($configKey) < 3) {
Logger::warning('EditResourceForm: invalid form key "' . $key . '" was ignored.');
continue;
}
$result[$configKey[2]] = $value;
}
}
return new Zend_Config($result);
}
}

View File

@ -142,4 +142,19 @@ class ResourceFactory implements ConfigAwareFactory
}
return $resource;
}
public static function ldapAvailable()
{
return extension_loaded('ldap');
}
public static function pgsqlAvailable()
{
return extension_loaded('pgsql');
}
public static function mysqlAvailable()
{
return extension_loaded('mysql');
}
}