diff --git a/.gitignore b/.gitignore index dd5d85c87..df2c1fb5b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ !.gitignore !.vagrant-puppet !public/.htaccess +!public/.htaccess.in !.gitkeep build/ @@ -30,3 +31,6 @@ config/enabledModules/ # User preferences config/preferences/*.ini + +# Application logfiles +var/log/*.log diff --git a/.vagrant-puppet/manifests/default.pp b/.vagrant-puppet/manifests/default.pp index bef6d8a42..c61228db0 100644 --- a/.vagrant-puppet/manifests/default.pp +++ b/.vagrant-puppet/manifests/default.pp @@ -42,7 +42,7 @@ user { 'icinga': } user { 'apache': - groups => 'icinga-cmd', + groups => ['icinga-cmd', 'vagrant'], require => [ Class['apache'], Group['icinga-cmd'] ] } @@ -471,13 +471,13 @@ exec { 'create-pgsql-icingaweb-db': exec { 'populate-icingaweb-mysql-db-accounts': unless => 'mysql -uicingaweb -picinga icingaweb -e "SELECT * FROM account;" &> /dev/null', - command => 'mysql -uicingaweb -picinga icingaweb < /vagrant/etc/schema/users.mysql.sql', + command => 'mysql -uicingaweb -picinga icingaweb < /vagrant/etc/schema/accounts.mysql.sql', require => [ Exec['create-mysql-icingaweb-db'] ] } exec { 'populate-icingweba-pgsql-db-accounts': 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/users.pgsql.sql', + command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/accounts.pgsql.sql', require => [ Exec['create-pgsql-icingaweb-db'] ] } diff --git a/Makefile.in b/Makefile.in index db9271bb8..cb7e4949c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -7,7 +7,7 @@ INSTALL_OPTS=@INSTALL_OPTS@ INSTALL_OPTS_WEB=@INSTALL_OPTS_WEB@ default: - @echo "Icinga2Web make targets: " + @echo "IcingaWeb make targets: " @printf "%b" " -install:\t\t\tInstall the application and overwrite configs\n" @printf "%b" " -update:\t\t\tInstall the application without touching the configs\n" @printf "%b" " -install-apache-config:\tInstall the apache configuration\n" @@ -15,7 +15,7 @@ default: # # Installs the whole application w\o httpd configurations # -install: install-static-files install-runtime-dirs +install: install-static-files install-runtime-dirs ensure-writable-folders update: install-application # @@ -26,8 +26,8 @@ clean: if [ -f ./Makefile ];then \ rm ./Makefile; \ fi; \ - if [ -f ./etc/apache/icinga2web.conf ];then \ - rm ./etc/apache/icinga2web.conf; \ + if [ -f ./etc/apache/icingaweb.conf ];then \ + rm ./etc/apache/icingaweb.conf; \ fi; # @@ -51,12 +51,12 @@ install-tests: copy-folder-tests # Install configurations for apache2 # install-apache-config: - $(INSTALL) -m 644 $(INSTALL_OPTS) "./etc/apache/icinga2web.conf" $(WWW_CONF_PATH)/icinga2web.conf; + $(INSTALL) -m 644 $(INSTALL_OPTS) "./etc/apache/icingaweb.conf" $(WWW_CONF_PATH)/icingaweb.conf; # # Installs the php files to the prefix # -install-application: copy-web-files-application copy-web-files-library +install-application: copy-web-files-application copy-web-files-library # # Rule for coying folders and containing files (arbitary types), hidden files are excluded @@ -76,6 +76,12 @@ copy-folder-%: done +ensure-writable-folders: + $(INSTALL) -m 775 $(INSTALL_OPTS_WEB) -d $(DESTDIR)$(prefix)/var/ + $(INSTALL) -m 775 $(INSTALL_OPTS_WEB) -d $(DESTDIR)$(prefix)/var/log + chmod -R 775 $(DESTDIR)$(prefix)/config + + # # Rule for copying only php, *html, js and ini files. Hidden files are ignored # @@ -84,14 +90,17 @@ copy-web-files-%: @dirs=`find ./$* -mindepth 1 -type d `;\ for dir in $$dirs; do \ - $(INSTALL) -m 755 $(INSTALL_OPTS) -d $(DESTDIR)$(prefix)/"$$dir"; \ + $(INSTALL) -m 755 $(INSTALL_OPTS_WEB) -d $(DESTDIR)$(prefix)/"$$dir"; \ done; @files=`find ./$* -mindepth 1 -type f \ - -name "*.php" -or -name "*.ini" -or -name "*.*html" -or -name "*.js" \ + -name "*.php" -or -name "*.ini" -or -name "*.*html" \ + -or -name "*.js" -or -name "*.css" -or -name "*.less" \ + -or -name "*.otf" -or -name "*.ttf" -or -name "*.otf" \ + -or -name "*.svg" -or -name "*.woff" \ -and ! -name ".*"`; \ for file in $$files; do \ - $(INSTALL) -m 644 $(INSTALL_OPTS) "$$file" $(DESTDIR)$(prefix)/"$$file"; \ + $(INSTALL) -m 644 $(INSTALL_OPTS_WEB) "$$file" $(DESTDIR)$(prefix)/"$$file"; \ done diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index f346f44cc..9068a1d39 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -63,7 +63,7 @@ class AuthenticationController extends ActionController $credentials = new Credentials(); $this->view->form = new LoginForm(); $this->view->form->setRequest($this->_request); - + $this->view->title = "Icinga Web Login"; try { $auth = AuthManager::getInstance(null, array( 'writeSession' => $this->modifiesSession diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index b90fcd055..381f20d43 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -24,8 +24,6 @@ */ // {{{ICINGA_LICENSE_HEADER}}} -use \Icinga\Application\Benchmark; -use \Icinga\Authentication\Manager; use \Icinga\Web\Controller\BaseConfigController; use \Icinga\Web\Widget\Tab; use \Icinga\Web\Url; @@ -33,10 +31,11 @@ use \Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; use \Icinga\Application\Icinga; use \Icinga\Application\Config as IcingaConfig; use \Icinga\Form\Config\GeneralForm; -use \Icinga\Form\Config\AuthenticationForm; +use \Icinga\Form\Config\Authentication\ReorderForm; use \Icinga\Form\Config\Authentication\LdapBackendForm; use \Icinga\Form\Config\Authentication\DbBackendForm; use \Icinga\Form\Config\LoggingForm; +use \Icinga\Form\Config\ConfirmRemovalForm; use \Icinga\Config\PreservingIniWriter; /** @@ -44,6 +43,7 @@ use \Icinga\Config\PreservingIniWriter; */ class ConfigController extends BaseConfigController { + /** * Create tabs for this configuration controller * @@ -97,9 +97,11 @@ class ConfigController extends BaseConfigController $form->setRequest($this->_request); if ($form->isSubmittedAndValid()) { if (!$this->writeConfigFile($form->getConfig(), 'config')) { - return false; + return; } - $this->redirectNow('/config'); + $this->view->successMessage = "Config Sucessfully Updated"; + $form->setConfiguration(IcingaConfig::app(), true); + } $this->view->form = $form; } @@ -114,11 +116,11 @@ class ConfigController extends BaseConfigController $form->setConfiguration(IcingaConfig::app()); $form->setRequest($this->_request); if ($form->isSubmittedAndValid()) { - $config = $form->getConfig(); if (!$this->writeConfigFile($form->getConfig(), 'config')) { return; } - $this->redirectNow('/config/logging'); + $this->view->successMessage = "Config Sucessfully Updated"; + $form->setConfiguration(IcingaConfig::app(), true); } $this->view->form = $form; } @@ -139,10 +141,12 @@ class ConfigController extends BaseConfigController */ public function moduleenableAction() { + $module = $this->_getParam('name'); $manager = Icinga::app()->getModuleManager(); - $manager->enableModule($this->_getParam('name')); - $manager->loadModule($this->_getParam('name')); - $this->redirectNow('config/moduleoverview?_render=body'); + $manager->enableModule($module); + $manager->loadModule($module); + $this->view->successMessage = 'Module "' . $module . '" enabled'; + $this->moduleoverviewAction(); } /** @@ -150,32 +154,78 @@ class ConfigController extends BaseConfigController */ public function moduledisableAction() { + $module = $this->_getParam('name'); $manager = Icinga::app()->getModuleManager(); - $manager->disableModule($this->_getParam('name')); - $this->redirectNow('config/moduleoverview?_render=body'); + $manager->disableModule($module); + $this->view->successMessage = 'Module "' . $module . '" disabled'; + $this->moduleoverviewAction(); } /** * Action for creating a new authentication backend */ - public function authenticationAction() + public function authenticationAction($showOnly = false) { - $form = new AuthenticationForm(); - $config = IcingaConfig::app('authentication'); - $form->setConfiguration($config); - $form->setRequest($this->_request); + $config = IcingaConfig::app('authentication', true); + $order = array_keys($config->toArray()); + $this->view->backends = array(); + + foreach ($config as $backend=>$backendConfig) { + $form = new ReorderForm(); + $form->setName('form_reorder_backend_' . $backend); + $form->setAuthenticationBackend($backend); + $form->setCurrentOrder($order); + $form->setRequest($this->_request); + + if (!$showOnly && $form->isSubmittedAndValid()) { + if ($this->writeAuthenticationFile($form->getReorderedConfig($config))) { + $this->view->successMessage = 'Authentication Order Updated'; + $this->authenticationAction(true); + } + return; + } + + $this->view->backends[] = (object) array( + 'name' => $backend, + 'reorderForm' => $form + ); + } + $this->render('authentication'); + } + + /** + * Action for removing a backend from the authentication list. + * + * Redirects to the overview after removal is finished + */ + public function removeauthenticationbackendAction() + { + $configArray = IcingaConfig::app('authentication', true)->toArray(); + $authBackend = $this->getParam('auth_backend'); + if (!isset($configArray[$authBackend])) { + $this->view->errorMessage = 'Can\'t perform removal: Unknown Authentication Backend Provided'; + $this->authenticationAction(true); + return; + } + + $form = new ConfirmRemovalForm(); + $form->setRequest($this->getRequest()); + $form->setRemoveTarget('auth_backend', $authBackend); + if ($form->isSubmittedAndValid()) { - $modifiedConfig = $form->getConfig(); - if (empty($modifiedConfig)) { - $form->addError('You need at least one authentication backend.'); - } else if (!$this->writeAuthenticationFile($modifiedConfig)) { - return; - } else { - $this->redirectNow('/config/authentication'); + unset($configArray[$authBackend]); + if ($this->writeAuthenticationFile($configArray)) { + $this->view->successMessage = 'Authentication Backend "' . $authBackend . '" Removed'; + $this->authenticationAction(true); } + return; } + $this->view->form = $form; + + $this->view->name = $authBackend; + $this->render('authentication/remove'); } /** @@ -188,18 +238,78 @@ class ConfigController extends BaseConfigController } else { $form = new DbBackendForm(); } + if ($this->getParam('auth_backend')) { + $form->setBackendName($this->getParam('auth_backend')); + } $form->setRequest($this->getRequest()); + + if ($form->isSubmittedAndValid()) { + $backendCfg = IcingaConfig::app('authentication')->toArray(); + foreach ($form->getConfig() as $backendName => $settings) { + if (isset($backendCfg[$backendName])) { + $this->view->errorMessage = 'Backend name already exists'; + $this->view->form = $form; + $this->render('authentication/create'); + return; + } + $backendCfg[$backendName] = $settings; + } + if ($this->writeAuthenticationFile($backendCfg)) { + // redirect to overview with success message + $this->view->successMessage = 'Backend Modification Written'; + $this->authenticationAction(true); + } + return; + } + $this->view->form = $form; + $this->render('authentication/create'); + } + + /** + * Form for editing backends + * + * Mostly the same like the createAuthenticationBackendAction, but with additional checks for backend existence + * and form population + */ + public function editauthenticationbackendAction() + { + $configArray = IcingaConfig::app('authentication', true)->toArray(); + $authBackend = $this->getParam('auth_backend'); + if (!isset($configArray[$authBackend])) { + $this->view->errorMessage = 'Can\'t edit: Unknown Authentication Backend Provided'; + $this->authenticationAction(true); + return; + } + + if ($configArray[$authBackend]['backend'] === 'ldap') { + $form = new LdapBackendForm(); + } else { + $form = new DbBackendForm(); + } + + $form->setBackendName($this->getParam('auth_backend')); + $form->setBackend(IcingaConfig::app('authentication', true)->$authBackend); + $form->setRequest($this->getRequest()); + if ($form->isSubmittedAndValid()) { $backendCfg = IcingaConfig::app('authentication')->toArray(); foreach ($form->getConfig() as $backendName => $settings) { $backendCfg[$backendName] = $settings; + // Remove the old section if the backend is renamed + if ($backendName != $authBackend) { + unset($backendCfg[$authBackend]); + } } - if (!$this->writeAuthenticationFile($backendCfg)) { - return; + if ($this->writeAuthenticationFile($backendCfg)) { + // redirect to overview with success message + $this->view->successMessage = 'Backend "' . $authBackend . '" created'; + $this->authenticationAction(true); } - $this->redirectNow('/config/authentication'); - + return; } + + + $this->view->name = $authBackend; $this->view->form = $form; $this->render('authentication/modify'); } diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php index 97f92ef3e..d8bb8c0b4 100644 --- a/application/controllers/PreferenceController.php +++ b/application/controllers/PreferenceController.php @@ -90,7 +90,7 @@ class PreferenceController extends BasePreferenceController } try { $userPreferences->commit(); - $this->view->success = true; + $this->view->successMessage = "Preferences Updated Successfully"; // recreate form to show new values $form = new GeneralForm(); diff --git a/application/forms/Authentication/LoginForm.php b/application/forms/Authentication/LoginForm.php index 66c665543..2d96b4f87 100644 --- a/application/forms/Authentication/LoginForm.php +++ b/application/forms/Authentication/LoginForm.php @@ -43,6 +43,7 @@ class LoginForm extends Form */ protected function create() { + $this->setName('form_login'); $this->addElement( 'text', 'username', diff --git a/application/forms/Config/Authentication/BaseBackendForm.php b/application/forms/Config/Authentication/BaseBackendForm.php index 01b0acb43..cd176504c 100644 --- a/application/forms/Config/Authentication/BaseBackendForm.php +++ b/application/forms/Config/Authentication/BaseBackendForm.php @@ -30,9 +30,7 @@ namespace Icinga\Form\Config\Authentication; use \Zend_Config; -use \Icinga\Application\Config as IcingaConfig; -use \Icinga\Application\Icinga; -use \Icinga\Application\Logger; +use \Icinga\Web\Form\Decorator\HelpText; use \Icinga\Application\DbAdapterFactory; use \Icinga\Web\Form; @@ -129,6 +127,48 @@ abstract class BaseBackendForm extends Form } } + /** + * 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); + } + + /** + * Validate this form with the Zend validation mechanism and perform a logic validation of the connection. + * + * If logic 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->isValidAuthenticationBackend()) { + $this->addForceCreationCheckbox(); + return false; + } + return true; + } + /** * Return an array containing all sections defined by this form as the key and all settings * as an key-value sub-array @@ -136,4 +176,14 @@ abstract class BaseBackendForm extends Form * @return array */ abstract public function getConfig(); + + /** + * Validate the configuration state of this backend with the concrete authentication backend. + * + * An implementation should not throw any exception, but use the add/setErrorMessages method of + * Zend_Form. If the 'backend_force_creation' checkbox is set, this method won't be called. + * + * @return bool True when validation succeeded, otherwise false + */ + abstract public function isValidAuthenticationBackend(); } diff --git a/application/forms/Config/Authentication/DbBackendForm.php b/application/forms/Config/Authentication/DbBackendForm.php index 3c09bccf6..ccd3a7093 100644 --- a/application/forms/Config/Authentication/DbBackendForm.php +++ b/application/forms/Config/Authentication/DbBackendForm.php @@ -29,12 +29,8 @@ namespace Icinga\Form\Config\Authentication; -use \Zend_Config; -use \Icinga\Application\Config as IcingaConfig; -use \Icinga\Application\Icinga; -use \Icinga\Application\Logger; +use \Icinga\Authentication\Backend\DbUserBackend; use \Icinga\Application\DbAdapterFactory; -use \Icinga\Web\Form; /** * Form class for adding/modifying database authentication backends @@ -66,6 +62,7 @@ class DbBackendForm extends BaseBackendForm */ public function create() { + $this->setName('form_modify_backend'); $name = $this->filterName($this->getBackendName()); $this->addElement( @@ -118,4 +115,32 @@ class DbBackendForm extends BaseBackendForm $section => $cfg ); } + + /** + * Validate the current configuration by creating a backend and requesting the user count + * + * @return bool True when the backend is valid, false otherwise + * @see BaseBackendForm::isValidAuthenticationBackend + */ + public function isValidAuthenticationBackend() + { + try { + $name = $this->getBackendName(); + $db = DbAdapterFactory::getDbAdapter( + $this->getValue('backend_' . $this->filterName($name) . '_' . 'resource') + ); + $dbBackend = new DbUserBackend($db); + if ($dbBackend->getUserCount() < 1) { + $this->addErrorMessage("No users found under the specified database backend"); + return false; + } + } catch (\Exception $e) { + $this->addErrorMessage("Using the specified backend failed: " . $e->getMessage()); + return false; + } catch (\Zend_Db_Statement_Exception $e) { + $this->addErrorMessage("Using the specified backend failed: " . $e->getMessage()); + return false; + } + return true; + } } diff --git a/application/forms/Config/Authentication/LdapBackendForm.php b/application/forms/Config/Authentication/LdapBackendForm.php index 087761bd9..f5667394a 100644 --- a/application/forms/Config/Authentication/LdapBackendForm.php +++ b/application/forms/Config/Authentication/LdapBackendForm.php @@ -28,11 +28,9 @@ namespace Icinga\Form\Config\Authentication; +use \Icinga\Authentication\Backend\LdapUserBackend; +use \Exception; use \Zend_Config; -use \Icinga\Application\Config as IcingaConfig; -use \Icinga\Application\Icinga; -use \Icinga\Application\Logger; -use \Icinga\Application\DbAdapterFactory; use \Icinga\Web\Form; /** @@ -47,6 +45,7 @@ class LdapBackendForm extends BaseBackendForm */ public function create() { + $this->setName('form_modify_backend'); $name = $this->filterName($this->getBackendName()); $backend = $this->getBackend(); @@ -133,6 +132,7 @@ class LdapBackendForm extends BaseBackendForm $this->setSubmitLabel('{{SAVE_ICON}} Save Backend'); } + /** * Return the ldap authentication backend configuration for this form * @@ -160,4 +160,33 @@ class LdapBackendForm extends BaseBackendForm $section => $cfg ); } + + /** + * Validate the current configuration by creating a backend and requesting the user count + * + * @return bool True when the backend is valid, false otherwise + * @see BaseBackendForm::isValidAuthenticationBacken + */ + public function isValidAuthenticationBackend() + { + try { + $cfg = $this->getConfig(); + $backendName = 'backend_' . $this->filterName($this->getBackendName()) . '_name'; + $testConn = new LdapUserBackend( + new Zend_Config($cfg[$this->getValue($backendName)]) + ); + + if ($testConn->getUserCount() === 0) { + throw new Exception('No Users Found On Directory Server'); + } + } catch (Exception $exc) { + + $this->addErrorMessage( + 'Connection Validation Failed:'. + $exc->getMessage() + ); + return false; + } + return true; + } } diff --git a/application/forms/Config/Authentication/ReorderForm.php b/application/forms/Config/Authentication/ReorderForm.php new file mode 100644 index 000000000..7c5f2ffb4 --- /dev/null +++ b/application/forms/Config/Authentication/ReorderForm.php @@ -0,0 +1,262 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Form\Config\Authentication; + +use \Zend_Config; +use \Icinga\Web\Form; +use \Icinga\Web\Url; + +/** + * Form for modifying the authentication provider order. + * + */ +class ReorderForm extends Form +{ + /** + * The name of the current backend which will get action buttons for up and down movement + * + * @var string + */ + private $backend = null; + + /** + * The current ordering of all backends, required to determine possible changes + * + * @var array + */ + private $currentOrder = array(); + + /** + * Set an array with the current order of all backends + * + * @param array $order An array containing backend names in the order they are defined in the authentication.ini + */ + public function setCurrentOrder(array $order) + { + $this->currentOrder = $order; + } + + /** + * Set the name of the authentication backend for which to create the form + * + * @param string $backend The name of the authentication backend + */ + public function setAuthenticationBackend($backend) + { + $this->backend = $backend; + } + + /** + * Return the name of the currently set backend as it will appear in the forms + * + * This calls the Zend Filtername function in order to filter specific chars + * + * @return string The filtered name of the backend + * @see Form::filterName() + */ + public function getBackendName() + { + return $this->filterName($this->backend); + } + + /** + * Create this form. + * + * Note: The form action will be set here to the authentication overview + * + * @see Form::create + */ + public function create() + { + $this->upForm = new Form(); + $this->downForm = new Form(); + + if ($this->moveElementUp($this->backend, $this->currentOrder) !== $this->currentOrder) { + + $this->upForm->addElement( + 'hidden', + 'form_backend_order', + array( + 'required' => true, + 'value' => join(',', $this->moveElementUp($this->backend, $this->currentOrder)) + ) + ); + $this->upForm->addElement( + 'submit', + 'btn_' . $this->getBackendName() . '_reorder_up', + array( + 'name' => 'btn_' . $this->getBackendName() . '_reorder_up', + 'label' => '{{ICON_UP}} Move up in authentication order', + + ) + ); + } + + if ($this->moveElementDown($this->backend, $this->currentOrder) !== $this->currentOrder) { + $this->downForm->addElement( + 'hidden', + 'form_backend_order', + array( + 'required' => true, + 'value' => join(',', $this->moveElementDown($this->backend, $this->currentOrder)) + ) + ); + $this->downForm->addElement( + 'submit', + 'btn_' . $this->getBackendName() . '_reorder_down', + array( + 'name' => 'btn_' . $this->getBackendName() . '_reorder_down', + 'label' => '{{ICON_UP}} Move down in authentication order', + + ) + ); + } + $this->setAction(Url::fromPath("config/authentication", array())->getAbsoluteUrl()); + } + + /** + * Return the result of $this->getValues but flatten the result + * + * The result will be a key=>value array without subarrays + * + * @param bool $supressArrayNotation passed to getValues + * + * @return array The currently set values + * @see Form::getValues() + */ + public function getFlattenedValues($supressArrayNotation = false) + { + $values = parent::getValues($supressArrayNotation); + $result = array(); + foreach ($values as $key => &$value) { + if (is_array($value)) { + $result += $value; + } else { + $result[$key] = $value; + } + } + return $result; + } + + /** + * Determine whether this form is submitted by testing the submit buttons of both subforms + * + * @return bool True when the form is submitted, otherwise false + */ + public function isSubmitted() + { + $checkData = $this->getRequest()->getParams(); + return isset ($checkData['btn_' . $this->getBackendName() . '_reorder_up']) || + isset ($checkData['btn_' . $this->getBackendName() . '_reorder_down']); + } + + /** + * Return the reordered configuration after a reorder button has been submited + * + * @param Zend_Config $config The configuration to reorder + * + * @return array An array containing the reordered configuration + */ + public function getReorderedConfig(Zend_Config $config) + { + $originalConfig = $config->toArray(); + $reordered = array(); + $newOrder = $this->getFlattenedValues(); + $order = explode(',', $newOrder['form_backend_order']); + foreach ($order as $key) { + if (!isset($originalConfig[$key])) { + continue; + } + $reordered[$key] = $originalConfig[$key]; + } + + return $reordered; + + } + + /** + * Static helper for moving an element in an array one slot up, if possible + * + * Example: + * + *
+     * $array = array('first', 'second', 'third');
+     * moveElementUp('third', $array); // returns ['first', 'third', 'second']
+     * 
+ * + * @param string $key The key to bubble up one slot + * @param array $array The array to work with + * + * @return array The modified array + */ + private static function moveElementUp($key, array $array) + { + $swap = null; + for ($i=0; $i + * $array = array('first', 'second', 'third'); + * moveElementDown('first', $array); // returns ['second', 'first', 'third'] + * + * + * @param string $key The key to bubble up one slot + * @param array $array The array to work with + * + * @return array The modified array + */ + private static function moveElementDown($key, array $array) + { + $swap = null; + for ($i=0; $i - * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 - * @author Icinga Development Team - */ -// {{{ICINGA_LICENSE_HEADER}}} - -namespace Icinga\Form\Config; - -use \Zend_Config; -use \Icinga\Application\Config as IcingaConfig; -use \Icinga\Application\Logger; -use \Icinga\Web\Form; -use \Icinga\Web\Form\Decorator\ConditionalHidden; - -/** - * Form for modifying the authentication provider and order. - * - * This is a composite form from one or more forms under the Authentication folder - */ -class AuthenticationForm extends Form -{ - /** - * The configuration to use for populating this form - * - * @var IcingaConfig - */ - private $config = null; - - /** - * The resources to use instead of the factory provided ones (use for testing) - * - * @var null - */ - private $resources = null; - - /** - * An array containing all provider subforms currently displayed - * - * @var array - */ - private $backendForms = array(); - - - /** - * Set an alternative array of resources that should be used instead of the DBFactory resource set - * (used for testing) - * - * @param array $resources The resources to use for populating the db selection field - */ - public function setResources(array $resources) - { - $this->resources = $resources; - } - - /** - * Set the configuration to be used for this form - * - * @param IcingaConfig $cfg - */ - public function setConfiguration($cfg) - { - $this->config = $cfg; - } - - /** - * Add a hint to remove the backend identified by $name - * - * The button will have the name "backend_$name_remove" - * - * @param string $name The backend to add this button for - * - * @return string The id of the added button - */ - private function addRemoveHint($name) - { - $this->addElement( - 'checkbox', - 'backend_' . $name . '_remove', - array( - 'name' => 'backend_' . $name . '_remove', - 'label' => 'Remove this authentication provider', - 'value' => $name, - 'checked' => $this->isMarkedForDeletion($name) - ) - ); - $this->enableAutoSubmit(array('backend_' . $name . '_remove')); - return 'backend_' . $name . '_remove'; - } - - /** - * Add the form for the provider identified by $name, with the configuration $backend - * - * Supported backends are backends with a form found under \Icinga\Form\Config\Authentication. - * The backend name ist the (uppercase first) prefix with 'BackendForm' as the suffix. - * - * Originally it was intended to add the provider as a subform. As this didn't really work with - * the Zend validation logic (maybe our own validation logic breaks it), we now create the form, but add - * all elements to this form explicitly. - * - * @param string $name The name of the backend to add - * @param Zend_Config $backend The configuration of the backend - */ - private function addProviderForm($name, $backend) - { - $type = ucfirst(strtolower($backend->get('backend'))); - $formClass = '\Icinga\Form\Config\Authentication\\' . $type . 'BackendForm'; - if (!class_exists($formClass)) { - Logger::error('Unsupported backend found in authentication configuration: ' . $backend->get('backend')); - return; - } - - $form = new $formClass(); - $form->setBackendName($name); - $form->setBackend($backend); - - if ($this->resources) { - $form->setResources($this->resources); - } - - // It would be nice to directly set the form via - // this->setForm, but Zend doesn't handle form validation - // properly if doing so. - $form->create(); - foreach ($form->getElements() as $elName => $element) { - if ($elName === 'backend_' . $this->filterName($name) . '_name') { - continue; - } - $this->addElement($element, $elName); - } - $this->backendForms[] = $form; - } - - /** - * Add the buttons for modifying authentication priorities - * - * @param string $name The name of the backend to add the buttons for - * @param array $order The current order which will be used to determine the changed order - * - * @return array An array containing the newly added form element ids as strings - */ - public function addPriorityButtons($name, $order = array()) - { - $formEls = array(); - $priorities = array( - 'up' => join(',', self::moveElementUp($name, $order)), - 'down' => join(',', self::moveElementDown($name, $order)) - ); - if ($priorities['up'] != join(',', $order)) { - $this->addElement( - 'button', - 'priority' . $name . '_up', - array( - 'name' => 'priority', - 'label' => 'Move up in authentication order', - 'value' => $priorities['up'], - 'type' => 'submit' - ) - ); - $formEls[] = 'priority' . $name . '_up'; - } - if ($priorities['down'] != join(',', $order)) { - $this->addElement( - 'button', - 'priority' . $name . '_down', - array( - 'name' => 'priority', - 'label' => 'Move down in authentication order', - 'value' => $priorities['down'], - 'type' => 'submit' - ) - ); - $formEls[] = 'priority' . $name . '_down'; - } - - return $formEls; - } - - /** - * Overwrite for Zend_Form::populate in order to preserve the modified priority of the backends - * - * @param array $values The values to populate the form with - * - * @return self - * - * @see Zend_Form::populate() - */ - public function populate(array $values) - { - $last_priority = $this->getValue('current_priority'); - parent::populate($values); - $this->getElement('current_priority')->setValue($last_priority); - return $this; - } - - /** - * Return an array containing all authentication providers in the order they should be used - * - * @return array An array containing the identifiers (section names) of the authentication backend in - * the order they should be persisted - */ - private function getAuthenticationOrder() - { - $request = $this->getRequest(); - $order = $request->getParam( - 'priority', - $request->getParam('current_priority', null) - ); - - if ($order === null) { - $order = array_keys($this->config->toArray()); - } else { - $order = explode(',', $order); - } - - return $order; - } - - /** - * Return true if the backend should be deleted when the changes are persisted - * - * @param string $backendName The name of the backend to check for being in a 'delete' state - * - * @return bool Whether this backend will be deleted on save - */ - private function isMarkedForDeletion($backendName) - { - return intval($this->getRequest()->getParam('backend_' . $backendName . '_remove', 0)) === 1; - } - - /** - * Add persistent values to the form in hidden fields - * - * Currently this adds the 'current_priority' field to persist priority modifications. This prevents changes in the - * authentication order to be lost as soon as other changes are submitted (like marking a backend for deletion) - */ - private function addPersistentState() - { - $this->addElement( - 'hidden', - 'current_priority', - array( - 'name' => 'current_priority', - 'value' => join(',', $this->getAuthenticationOrder()) - ) - ); - } - - /** - * Create the authentication provider configuration form - * - * @see IcingaForm::create() - */ - public function create() - { - $order = $this->getAuthenticationOrder(); - - foreach ($order as $name) { - $this->addNote('Backend ' . $name, 4); - $this->addRemoveHint($this->filterName($name)); - $backend = $this->config->get($name, null); - if ($backend === null) { - continue; - } - if (!$this->isMarkedForDeletion($this->filterName($name))) { - $this->addProviderForm($name, $backend); - $this->addPriorityButtons($name, $order); - } - } - - $this->addPersistentState(); - $this->enableConditionalDecorator(); - $this->setSubmitLabel('{{SAVE_ICON}} Save Changes'); - } - - /** - * Return the configuration state defined by this form - * - * @return array - */ - public function getConfig() - { - $result = array(); - foreach ($this->backendForms as $name) { - - $name->populate($this->getRequest()->getParams()); - $result += $name->getConfig(); - - } - return $result; - } - - /** - * Enable the "ConditionalHidden" Decorator for all elements in this form - * - * @see ConditionalHidden - */ - private function enableConditionalDecorator() - { - foreach ($this->getElements() as $element) { - $element->addDecorator(new ConditionalHidden()); - } - } - - /** - * Static helper for moving an element in an array one slot up, if possible - * - * Example: - * - *
-     * $array = array('first', 'second', 'third');
-     * moveElementUp('third', $array); // returns ['first', 'third', 'second']
-     * 
- * - * @param string $key The key to bubble up one slot - * @param array $array The array to work with - * - * @return array The modified array - */ - private static function moveElementUp($key, array $array) - { - $swap = null; - for ($i=0; $i - * $array = array('first', 'second', 'third'); - * moveElementDown('first', $array); // returns ['second', 'first', 'third'] - * - * - * @param string $key The key to bubble up one slot - * @param array $array The array to work with - * - * @return array The modified array - */ - private static function moveElementDown($key, array $array) - { - $swap = null; - for ($i=0; $i + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + + +namespace Icinga\Form\Config; + +use Icinga\Web\Form; + +/** + * Form for confirming removal of an object + */ +class ConfirmRemovalForm extends Form +{ + /** + * The value of the target to remove + * + * @var string + */ + private $removeTarget; + + /** + * The name of the target parameter to remove + * + * @var string + */ + private $targetName; + + /** + * Set the remove target in this field to be a hidden field with $name and value $target + * + * @param string $name The name to be set in the hidden field + * @param string $target The value to be set in the hidden field + */ + public function setRemoveTarget($name, $target) + { + $this->targetName = $name; + $this->removeTarget = $target; + } + + /** + * Create the confirmation form + * + * @see Form::create() + */ + public function create() + { + $this->setName('form_confirm_removal'); + $this->addElement( + 'hidden', + $this->targetName, + array( + 'value' => $this->removeTarget, + 'required' => true + ) + ); + $this->setSubmitLabel('{{REMOVE_ICON}} Confirm Removal'); + } +} diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php index 031f0fdb5..f80a12c65 100644 --- a/application/forms/Config/GeneralForm.php +++ b/application/forms/Config/GeneralForm.php @@ -33,7 +33,6 @@ use \Zend_Config; use \Zend_Form_Element_Text; use \Zend_Form_Element_Select; use \Icinga\Application\Config as IcingaConfig; -use \Icinga\Application\Icinga; use \Icinga\Application\DbAdapterFactory; use \Icinga\Web\Form; use \Icinga\Web\Form\Validator\WritablePathValidator; @@ -322,7 +321,7 @@ class GeneralForm extends Form if ($preferences === null) { $preferences = new Zend_Config(array()); } - + $this->setName('form_config_general'); $this->addDevelopmentCheckbox($global); $this->addTimezoneSelection($global); $this->addModuleSettings($global); diff --git a/application/forms/Config/LoggingForm.php b/application/forms/Config/LoggingForm.php index 7528031f0..6183fc4c4 100644 --- a/application/forms/Config/LoggingForm.php +++ b/application/forms/Config/LoggingForm.php @@ -122,6 +122,7 @@ class LoggingForm extends Form */ public function create() { + $this->setName('form_config_logging'); if ($this->config === null) { $this->config = new Zend_Config(array()); } diff --git a/application/forms/Dashboard/AddUrlForm.php b/application/forms/Dashboard/AddUrlForm.php index 1784517f9..af6f5fc3f 100644 --- a/application/forms/Dashboard/AddUrlForm.php +++ b/application/forms/Dashboard/AddUrlForm.php @@ -129,7 +129,7 @@ class AddUrlForm extends Form protected function create() { $dashboard = new Dashboard(); - + $this->setName('form_dashboard_add'); $dashboard->readConfig(IcingaConfig::app('dashboard/dashboard')); $this->addElement( 'text', @@ -144,7 +144,8 @@ class AddUrlForm extends Form if (empty($elems) || // show textfield instead of combobox when no pane is available ($this->getRequest()->getPost('create_new_pane', '0') && // or when a new pane should be created (+ button) - !$this->getRequest()->getPost('use_existing_dashboard', '0')) // and the user didn't click the 'use existing' button + !$this->getRequest()->getPost('use_existing_dashboard', '0')) // and the user didn't click the 'use + // existing' button ) { $this->addNewPaneTextField(); } else { diff --git a/application/forms/Preference/GeneralForm.php b/application/forms/Preference/GeneralForm.php index 5d14429bf..9c43a2bbc 100644 --- a/application/forms/Preference/GeneralForm.php +++ b/application/forms/Preference/GeneralForm.php @@ -34,7 +34,6 @@ use \Zend_Form_Element_Text; use \Zend_Form_Element_Select; use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; -use \Icinga\Application\DbAdapterFactory; use \Icinga\User\Preferences; use \Icinga\Web\Form; use \Icinga\Web\Form\Validator\TimeFormatValidator; @@ -215,6 +214,7 @@ class GeneralForm extends Form if ($this->config === null) { $this->config = new Zend_Config(array()); } + $this->setName('form_preference_set'); $global = $this->config->global; if ($global === null) { $global = new Zend_Config(array()); diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml index 3f842f5cd..02798122a 100755 --- a/application/layouts/scripts/layout.phtml +++ b/application/layouts/scripts/layout.phtml @@ -11,7 +11,7 @@ - + <?= $this->title ? $this->title : 'Icinga Web'; ?> diff --git a/application/layouts/scripts/parts/topbar.phtml b/application/layouts/scripts/parts/topbar.phtml index 1648ea346..35698cb92 100755 --- a/application/layouts/scripts/parts/topbar.phtml +++ b/application/layouts/scripts/parts/topbar.phtml @@ -13,17 +13,19 @@ diff --git a/application/views/scripts/config/authentication.phtml b/application/views/scripts/config/authentication.phtml index c48726d4a..9dd267da5 100644 --- a/application/views/scripts/config/authentication.phtml +++ b/application/views/scripts/config/authentication.phtml @@ -1,35 +1,56 @@ 'ldap') -)->getAbsoluteUrl(); - -$createDbBackend = Url::fromPath( - '/config/createAuthenticationBackend', - array('type' => 'db') -)->getAbsoluteUrl(); +$createLdapBackend = $this->href('/config/createAuthenticationBackend', array('type' => 'ldap')); +$createDbBackend = $this->href('/config/createAuthenticationBackend', array('type' => 'db')); ?> + tabs->render($this); ?> -form->getErrorMessages(); - if (!empty($errors)): -?> -
-

{{ERROR_ICON}} There are errors in your configuration:

-
-
    - -
  • - -
-
+errorMessage): ?> +
+ {{ERROR_ICON}} + escape($this->errorMessage); ?> +
-
- {{CREATE_ICON}} Create A New LDAP Authentication Backend
- {{CREATE_ICON}} Create A New DB Authentication Backend + +successMessage): ?> +
+ {{OK_ICON}} + escape($this->successMessage); ?>
-form ?> \ No newline at end of file + + + + +backends as $backend): ?> +
+
+ Backend escape($backend->name); ?> +
+ reorderForm; ?> +
+ +
+ \ No newline at end of file diff --git a/application/views/scripts/config/authentication/create.phtml b/application/views/scripts/config/authentication/create.phtml new file mode 100644 index 000000000..5aa84abbf --- /dev/null +++ b/application/views/scripts/config/authentication/create.phtml @@ -0,0 +1,15 @@ +tabs->render($this); ?> + +

{{CREATE_ICON}} Create New Authentication Backend

+ +errorMessage): ?> +
+ escape($this->errorMessage); ?> +
+ + +

+ Create a new backend for authenticating your users. This backend will be added at the end of your authentication order. +

+ +form ?> \ No newline at end of file diff --git a/application/views/scripts/config/authentication/modify.phtml b/application/views/scripts/config/authentication/modify.phtml index 1d36529a9..7c8f6c7cf 100644 --- a/application/views/scripts/config/authentication/modify.phtml +++ b/application/views/scripts/config/authentication/modify.phtml @@ -1,4 +1,14 @@ tabs->render($this); ?> -

{{CREATE_ICON}} Create New Backend

+

{{CREATE_ICON}} Edit Backend "escape($this->name); ?>"

+ +errorMessage || $this->form->getErrorMessages()): ?> +
+ escape($this->errorMessage); ?> + form->getErrorMessages() as $error): ?> + escape($error); ?>
+ +
+ + form ?> \ No newline at end of file diff --git a/application/views/scripts/config/authentication/remove.phtml b/application/views/scripts/config/authentication/remove.phtml new file mode 100644 index 000000000..3a3cc27ce --- /dev/null +++ b/application/views/scripts/config/authentication/remove.phtml @@ -0,0 +1,4 @@ +tabs->render($this); ?> + +

{{REMOVE_ICON}} Remove Backend "escape($this->name); ?>"

+form ?> \ No newline at end of file diff --git a/application/views/scripts/config/index.phtml b/application/views/scripts/config/index.phtml index f0b046499..603a496aa 100644 --- a/application/views/scripts/config/index.phtml +++ b/application/views/scripts/config/index.phtml @@ -1,2 +1,10 @@ tabs->render($this); ?> + +successMessage): ?> +
+ {{OK_ICON}} + escape($this->successMessage); ?> +
+ + form ?> \ No newline at end of file diff --git a/application/views/scripts/config/logging.phtml b/application/views/scripts/config/logging.phtml index 94f220abd..98766d479 100644 --- a/application/views/scripts/config/logging.phtml +++ b/application/views/scripts/config/logging.phtml @@ -2,6 +2,13 @@ form->getErrorMessages(); ?> +successMessage): ?> +
+ {{OK_ICON}} + escape($this->successMessage); ?> +
+ +

Errors occured when trying to save the project.

diff --git a/application/views/scripts/config/module/overview.phtml b/application/views/scripts/config/module/overview.phtml index 3ba07a2dc..652967b8d 100644 --- a/application/views/scripts/config/module/overview.phtml +++ b/application/views/scripts/config/module/overview.phtml @@ -8,6 +8,14 @@ $modules = $this->modules->paginate(); tabs->render($this); ?>

Installed Modules

+ +successMessage): ?> +
+ {{OK_ICON}} + escape($this->successMessage); ?> +
+ + paginationControl($modules, null, null, array( 'preserve' => $this->preserve )); diff --git a/application/views/scripts/preference/index.phtml b/application/views/scripts/preference/index.phtml index d83e1bd97..0a3749eb8 100644 --- a/application/views/scripts/preference/index.phtml +++ b/application/views/scripts/preference/index.phtml @@ -1,8 +1,8 @@ tabs->render($this); ?> -success)) : ?> +successMessage)) : ?>
-

Preferences Updated Successfully

+

{{SUCCESS_ICON}} successMessage ?>

diff --git a/config/authentication.ini.in b/config/authentication.ini.in old mode 100644 new mode 100755 index ce1bb3222..e65561981 --- a/config/authentication.ini.in +++ b/config/authentication.ini.in @@ -1,24 +1,33 @@ -[ldap-authentication] -backend=ldap -hostname=@ldap_host@ -port=@ldap_port@ -root_dn='@ldap_rootdn@' -bind_dn='@ldap_binddn@' -bind_pw=@ldap_bindpass@ +; authentication.ini +; +; Each section listed in this configuration represents a single backend +; that can be used to authenticate users or groups. Each databse backend must refer +; to a resource defined in resources.ini, +; +; The order of entries in this configuration is used to determine the fallback +; priority in case of an error. If the resource referenced in the first +; entry is not reachable, the next lower entry will be used for authentication. +; Please be aware that this behaviour is not valid for the authentication itself. +; The authentication will only be done against the one available resource with the highest +; priority. + + +@use_ldap_auth@[ldap_authentication] +@use_ldap_auth@backend = "ldap" +@use_ldap_auth@target = "user" +@use_ldap_auth@hostname = "@ldap_host@" +@use_ldap_auth@port = "@ldap_port@" +@use_ldap_auth@root_dn = "@ldap_rootdn@" +@use_ldap_auth@bind_dn = "@ldap_binddn@" +@use_ldap_auth@bind_pw = "@ldap_bindpass@" ; Attributes for ldap user lookup -user.ldap_object_class=@ldap_user_objectclass@ -user.ldap_name_attribute=@ldap_attribute_username@ -user.password_attribute=@ldap_attribute_password@ -group.ldap_object_class=@ldap_group_objectclass@ -group.ldap_name_attribute=@ldap_attribute_groupname@ +@use_ldap_auth@user_class = "@ldap_user_objectclass@" +@use_ldap_auth@user_name_attribute = "@ldap_attribute_username@" +@use_ldap_auth@user_password_attribute = "@ldap_attribute_password@" -[internal-authentication] -backend=db -type=@internal_db_type@ -host=@internal_db_host@ -port=@internal_db_port@ -database=@internal_db_name@ -user=@internal_db_user@ -password=@internal_db_pass@ +@use_internal_auth@[internal_authentication] +@use_internal_auth@backend = db +@use_internal_auth@target = "user" +@use_internal_auth@resource = "internal_db" diff --git a/config/config.ini b/config/config.ini index e10e68a6f..0e4407bd8 100755 --- a/config/config.ini +++ b/config/config.ini @@ -1,36 +1,38 @@ [global] -environment = "development" -timezone = "Europe/Berlin" -indexModule = "monitoring" -indexController = "dashboard" +environment = "development" +timezone = "Europe/Berlin" +indexModule = "monitoring" +indexController = "dashboard" ; The moduleFolder directive is currently not used anywhere but configureable ; via the frontend and this file. With feature #4607 moduleFolder will ; be replaced with a configuration directive for locations of ; installed modules moduleFolder = "/etc/icinga2-web/enabledModules" -dateFormat = "d/m/Y" -timeFormat = "g:i A" +dateFormat = "d/m/Y" +timeFormat = "g:i A" + [logging] ; General log -enable = "1" -type = "stream" -verbose = "1" -target = "/tmp/icinga2.log" +enable = "1" +type = "stream" +verbose = "1" +target = "{app}/var/log/icinga.log" ; For development and debug purposes: Logs additional (non critical) events to a ; seperate log -debug.enable = "1" -debug.type = "stream" -debug.target = "/tmp/icinga2.debug.log" +debug.enable = "1" +debug.type = "stream" +debug.target = "{app}/var/log/icinga.debug.log" + ; Use ini store to store preferences on local disk [preferences] -type = "ini" +type = "ini" ; Use database to store preference into mysql or postgres ;[preferences] ;type=db ;resource=icingaweb-mysql -configPath = "/vagrant/config/preferences" +configPath = "{app}/config/preferences" diff --git a/config/modules/monitoring/backends.ini.in b/config/modules/monitoring/backends.ini.in index 2d33fcecc..c763f076a 100644 --- a/config/modules/monitoring/backends.ini.in +++ b/config/modules/monitoring/backends.ini.in @@ -1,18 +1,15 @@ [localdb] type = ido resource = "ido" -@ido_enabled@ [locallive] type = livestatus socket = @livestatus_socket@ -@livestatus_enabled@ [localfile] type = statusdat status_file = @statusdat_file@ objects_file = @objects_cache_file@ -@statusdat_enabled@ ;[localfailsafe] ;enabled=false diff --git a/config/resources.ini.in b/config/resources.ini.in index dedfa61b6..5cf86e817 100644 --- a/config/resources.ini.in +++ b/config/resources.ini.in @@ -3,7 +3,7 @@ ; The configuration file *resources.ini* contains data sources that ; can be referenced in other configurations. This allows you to manage ; all connections to SQL databases in one single place, avoiding the need -: to edit several different configuration files, when the connection +; to edit several different configuration files, when the connection ; information of a resource change. ; ; Each section represents a resource, with the section name being the @@ -13,21 +13,13 @@ ; be interpreted. Currently only the resource type *db* is available. -[icingaweb-pgsql] +[internal_db] type = db -db = pgsql ; PostgreSQL -host = localhost -password = icinga -username = icingaweb -dbname = icingaweb - -[icingaweb-mysql] -type = db -db = mysql ; MySQL -host = localhost -password = icinga -username = icingaweb -dbname = icingaweb +db = @internal_db_type@ +host = @internal_db_host@ +password = @internal_db_pass@ +username = @internal_db_user@ +dbname = @internal_db_name@ [ido] type = db diff --git a/configure b/configure index 3e077c413..37dff76ec 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Icinga2Web 1.0.0. +# Generated by GNU Autoconf 2.69 for IcingaWeb 1.0.0. # # Report bugs to . # @@ -578,22 +578,20 @@ MFLAGS= MAKEFLAGS= # Identity of this package. -PACKAGE_NAME='Icinga2Web' -PACKAGE_TARNAME='icinga2web' +PACKAGE_NAME='IcingaWeb' +PACKAGE_TARNAME='icingaweb' PACKAGE_VERSION='1.0.0' -PACKAGE_STRING='Icinga2Web 1.0.0' +PACKAGE_STRING='IcingaWeb 1.0.0' PACKAGE_BUGREPORT='info@icinga.org' PACKAGE_URL='' -ac_default_prefix=/usr/local/icinga2-web +ac_default_prefix=/usr/local/icingaweb ac_subst_vars='LTLIBOBJS LIBOBJS INSTALL_OPTS_WEB INSTALL_OPTS -ldap_enabled -ido_enabled -statusdat_enabled -livestatus_enabled +use_internal_auth +use_ldap_auth icinga_commandpipe livestatus_socket objects_cache_file @@ -622,7 +620,7 @@ internal_db_port internal_db_host internal_db_name internal_db_type -icinga2web_config_path +icingaweb_config_path bin_group bin_user www_conf_path @@ -677,7 +675,7 @@ SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking -with_icinga2web_config_path +with_icingaweb_config_path with_web_user with_web_group with_web_path @@ -1258,7 +1256,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Icinga2Web 1.0.0 to adapt to many kinds of systems. +\`configure' configures IcingaWeb 1.0.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1306,7 +1304,7 @@ Fine tuning of the installation directories: --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/icinga2web] + --docdir=DIR documentation root [DATAROOTDIR/doc/icingaweb] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] @@ -1319,18 +1317,18 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Icinga2Web 1.0.0:";; + short | recursive ) echo "Configuration of IcingaWeb 1.0.0:";; esac cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-icinga2web-config-path + --with-icingaweb-config-path Configuration path for icinga --with-web-user=USER username for web writable files (default www-data) --with-web-group=GROUP group for web writable files (default www-data) - --with-web-path=PATH web sub path (default /cranberry) + --with-web-path=PATH web sub path (default /icingaweb) --with-http-configuration-path=PATH Include folder apache2 (default /etc/apache2/conf.d) --with-bin-user=USER user for all other files (default root) @@ -1340,7 +1338,7 @@ Optional Packages: mysql, supported: pgsql, mysql) --with-internal-db-name=NAME database name to use for internal database (default - icinga2web) + icingaweb) --with-internal-db-host=HOST database host to use for internal database (default localhost) @@ -1349,10 +1347,10 @@ Optional Packages: 3306 for mysql, 5432 for pgsql) --with-internal-db-pass=PASS database pass to use for internal database (default - icinga2web) + icingaweb) --with-internal-db-user=USER database user to use for internal database (default - icinga2web) + icingaweb) --with-internal-authentication use the internal database for authentication (default: yes) @@ -1487,7 +1485,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Icinga2Web configure 1.0.0 +IcingaWeb configure 1.0.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1504,7 +1502,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Icinga2Web $as_me 1.0.0, which was +It was created by IcingaWeb $as_me 1.0.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -1855,7 +1853,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "x$prefix" = "xNONE" ; then - installDir="/usr/local/icinga2-web" + installDir="/usr/local/icingaweb" prefix=$installDir else installDir=$prefix @@ -2210,11 +2208,11 @@ fi # Configuration files # -# Check whether --with-icinga2web_config_path was given. -if test "${with_icinga2web_config_path+set}" = set; then : - withval=$with_icinga2web_config_path; icinga2web_config_path="'$withval'" +# Check whether --with-icingaweb_config_path was given. +if test "${with_icingaweb_config_path+set}" = set; then : + withval=$with_icingaweb_config_path; icingaweb_config_path="'$withval'" else - icinga2web_config_path="'$prefix/config/'" + icingaweb_config_path="'$prefix/config/'" fi @@ -2274,7 +2272,7 @@ fi if test "${with_web_path+set}" = set; then : withval=$with_web_path; web_path=$withval else - web_path=/cranberry + web_path=/icingaweb fi @@ -2346,7 +2344,7 @@ fi if test "${with_internal_db_name+set}" = set; then : withval=$with_internal_db_name; internal_db_name=$withval else - internal_db_name=icinga2web + internal_db_name=icingaweb fi @@ -2376,7 +2374,7 @@ fi if test "${with_internal_db_pass+set}" = set; then : withval=$with_internal_db_pass; internal_db_pass=$withval else - internal_db_pass=icinga2web + internal_db_pass=icingaweb fi @@ -2386,7 +2384,7 @@ fi if test "${with_internal_db_user+set}" = set; then : withval=$with_internal_db_user; internal_db_user=$withval else - internal_db_user=icinga2web + internal_db_user=icingaweb fi @@ -2400,7 +2398,7 @@ fi if test "${with_internal_authentication+set}" = set; then : withval=$with_internal_authentication; internal_authentication=yes else - internal_authentication=def + internal_authentication=yes fi @@ -2444,7 +2442,7 @@ fi if test "${with_ldap_binddn+set}" = set; then : withval=$with_ldap_binddn; ldap_binddn=$withval else - ldap_binddn="cn=Manager, ou=icinga, ou=org" + ldap_binddn="cn=admin,cn=config" fi @@ -2454,7 +2452,7 @@ fi if test "${with_ldap_bindpass+set}" = set; then : withval=$with_ldap_bindpass; ldap_bindpass=$withval else - ldap_bindpass="secret" + ldap_bindpass="admin" fi @@ -2818,7 +2816,6 @@ fi ido_enabled="disable=1" statusdat_enabled="disable=1" livestatus_enabled="disable=1" -ldap_enabled="disable=1" case $icinga_backend in #( "ido") : @@ -2831,6 +2828,7 @@ case $icinga_backend in #( statusdat_enabled="" ;; esac +use_ldap_auth=";" if test "x$ldap_authentication" != xno; then : for x in ldap;do @@ -2844,7 +2842,25 @@ else fi done - ldap_enabled="" + use_ldap_auth="" + +fi + +use_internal_auth=";" +if test "x$internal_authentication" != xno; then : + + for x in ldap;do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if php has $x module" >&5 +$as_echo_n "checking if php has $x module... " >&6; } + if php -m | $GREP -iq "^$x$" ; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } +else + as_fn_error $? "not found" "$LINENO" 5 +fi + done + + use_internal_auth="" fi @@ -2909,55 +2925,6 @@ fi - -# Internal db setup - - - - - - - -# ldap setup - - - - - - - - - - - - -# backend setup - - -# ido backend variables - - - - - - - -# status.dat backend - - - -# livestatus backend - - -# command pipe - - -# Comment placeholders for toggling backends - - - - - # Application and installation @@ -2966,7 +2933,7 @@ fi # # Create config files # -ac_config_files="$ac_config_files Makefile config/authentication.ini config/resources.ini config/modules/monitoring/backends.ini public/index.php" +ac_config_files="$ac_config_files Makefile config/authentication.ini config/resources.ini config/modules/monitoring/backends.ini etc/apache/icingaweb.conf public/.htaccess" # @@ -3514,7 +3481,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Icinga2Web $as_me 1.0.0, which was +This file was extended by IcingaWeb $as_me 1.0.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -3567,7 +3534,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Icinga2Web config.status 1.0.0 +IcingaWeb config.status 1.0.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -3682,7 +3649,8 @@ do "config/authentication.ini") CONFIG_FILES="$CONFIG_FILES config/authentication.ini" ;; "config/resources.ini") CONFIG_FILES="$CONFIG_FILES config/resources.ini" ;; "config/modules/monitoring/backends.ini") CONFIG_FILES="$CONFIG_FILES config/modules/monitoring/backends.ini" ;; - "public/index.php") CONFIG_FILES="$CONFIG_FILES public/index.php" ;; + "etc/apache/icingaweb.conf") CONFIG_FILES="$CONFIG_FILES etc/apache/icingaweb.conf" ;; + "public/.htaccess") CONFIG_FILES="$CONFIG_FILES public/.htaccess" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac diff --git a/configure.ac b/configure.ac index 910739af1..215dd0a51 100755 --- a/configure.ac +++ b/configure.ac @@ -2,11 +2,11 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) -AC_INIT([Icinga2Web], [1.0.0], [info@icinga.org]) -AC_PREFIX_DEFAULT(/usr/local/icinga2-web) +AC_INIT([IcingaWeb], [1.0.0], [info@icinga.org]) +AC_PREFIX_DEFAULT(/usr/local/icingaweb) if test "x$prefix" = "xNONE" ; then - installDir="/usr/local/icinga2-web" + installDir="/usr/local/icingaweb" prefix=$installDir else installDir=$prefix @@ -29,10 +29,10 @@ AC_CHECK_PHP_MODULE([sockets json]) # # Configuration files # -AC_ARG_WITH([icinga2web_config_path], - AS_HELP_STRING([--with-icinga2web-config-path], [Configuration path for icinga ]), - icinga2web_config_path="'$withval'", - icinga2web_config_path="'$prefix/config/'" +AC_ARG_WITH([icingaweb_config_path], + AS_HELP_STRING([--with-icingaweb-config-path], [Configuration path for icinga ]), + icingaweb_config_path="'$withval'", + icingaweb_config_path="'$prefix/config/'" ) # @@ -52,9 +52,9 @@ AC_ARG_WITH([web_group], ) AC_ARG_WITH([web_path], - AS_HELP_STRING([--with-web-path=PATH], [web sub path (default /cranberry)]), + AS_HELP_STRING([--with-web-path=PATH], [web sub path (default /icingaweb)]), web_path=$withval, - web_path=/cranberry + web_path=/icingaweb ) AC_ARG_WITH([www_conf_path], @@ -90,9 +90,9 @@ AC_ARG_WITH([internal_db_type], ) AC_ARG_WITH([internal_db_name], - AS_HELP_STRING([--with-internal-db-name=NAME], [database name to use for internal database (default icinga2web)]), + AS_HELP_STRING([--with-internal-db-name=NAME], [database name to use for internal database (default icingaweb)]), internal_db_name=$withval, - internal_db_name=icinga2web + internal_db_name=icingaweb ) AC_ARG_WITH([internal_db_host], @@ -108,15 +108,15 @@ AC_ARG_WITH([internal_db_port], ) AC_ARG_WITH([internal_db_pass], - AS_HELP_STRING([--with-internal-db-pass=PASS], [database pass to use for internal database (default icinga2web)]), + AS_HELP_STRING([--with-internal-db-pass=PASS], [database pass to use for internal database (default icingaweb)]), internal_db_pass=$withval, - internal_db_pass=icinga2web + internal_db_pass=icingaweb ) AC_ARG_WITH([internal_db_user], - AS_HELP_STRING([--with-internal-db-user=USER], [database user to use for internal database (default icinga2web)]), + AS_HELP_STRING([--with-internal-db-user=USER], [database user to use for internal database (default icingaweb)]), internal_db_user=$withval, - internal_db_user=icinga2web + internal_db_user=icingaweb ) # @@ -126,7 +126,7 @@ AC_ARG_WITH([internal_db_user], AC_ARG_WITH([internal_authentication], AC_HELP_STRING([--with-internal-authentication], [use the internal database for authentication (default: yes)]), internal_authentication=yes, - internal_authentication=def + internal_authentication=yes ) AC_ARG_WITH([ldap_authentication], @@ -154,13 +154,13 @@ AC_ARG_WITH([ldap_port], AC_ARG_WITH([ldap_binddn], AS_HELP_STRING([--with-ldap-binddn=DN], [dn to use for retrieving user information via ldap (default cn=admin, cn=config)]), ldap_binddn=$withval, - ldap_binddn=["cn=Manager, ou=icinga, ou=org"] + ldap_binddn=["cn=admin,cn=config"] ) AC_ARG_WITH([ldap_bindpass], AS_HELP_STRING([--with-ldap-bindpass=PASS], [password to use for retrieving user information via ldap (default admin)]), ldap_bindpass=$withval, - ldap_bindpass=["secret"] + ldap_bindpass=["admin"] ) AC_ARG_WITH([ldap_rootdn], @@ -346,7 +346,6 @@ AS_IF([test "x$ido_db_type" = xpgsql], [ ido_enabled="disable=1" statusdat_enabled="disable=1" livestatus_enabled="disable=1" -ldap_enabled="disable=1" AS_CASE([$icinga_backend], ["ido"], [ido_enabled=""], @@ -354,9 +353,16 @@ AS_CASE([$icinga_backend], ["livestatus"], [livestatus_enabled=""], [statusdat_enabled=""]) +use_ldap_auth=";" AS_IF([test "x$ldap_authentication" != xno], AC_CHECK_PHP_MODULE([ldap]) - ldap_enabled="" + use_ldap_auth="" +) + +use_internal_auth=";" +AS_IF([test "x$internal_authentication" != xno], + AC_CHECK_PHP_MODULE([ldap]) + use_internal_auth="" ) # @@ -371,7 +377,7 @@ AC_SUBST(web_path) AC_SUBST(www_conf_path) AC_SUBST(bin_user) AC_SUBST(bin_group) -AC_SUBST(icinga2web_config_path) +AC_SUBST(icingaweb_config_path) # Internal db setup AC_SUBST(internal_db_type) @@ -416,58 +422,9 @@ AC_SUBST(livestatus_socket) AC_SUBST(icinga_commandpipe) # Comment placeholders for toggling backends -AC_SUBST(livestatus_enabled) -AC_SUBST(statusdat_enabled) -AC_SUBST(ido_enabled) -AC_SUBST(ldap_enabled) -# Internal db setup -AC_SUBST(internal_db_type) -AC_SUBST(internal_db_name) -AC_SUBST(internal_db_host) -AC_SUBST(internal_db_port) -AC_SUBST(internal_db_user) -AC_SUBST(internal_db_pass) - -# ldap setup -AC_SUBST(ldap_host) -AC_SUBST(ldap_port) -AC_SUBST(ldap_rootdn) -AC_SUBST(ldap_binddn) -AC_SUBST(ldap_bindpass) -AC_SUBST(ldap_user_objectclass) -AC_SUBST(ldap_attribute_basedn) -AC_SUBST(ldap_attribute_username) -AC_SUBST(ldap_attribute_password) -AC_SUBST(ldap_group_objectclass) -AC_SUBST(ldap_attribute_groupname) - -# backend setup -AC_SUBST(icinga_backend) - -# ido backend variables -AC_SUBST(ido_db_type) -AC_SUBST(ido_host) -AC_SUBST(ido_port) -AC_SUBST(ido_database) -AC_SUBST(ido_user) -AC_SUBST(ido_password) - -# status.dat backend -AC_SUBST(statusdat_file) -AC_SUBST(objects_cache_file) - -# livestatus backend -AC_SUBST(livestatus_socket) - -# command pipe -AC_SUBST(icinga_commandpipe) - -# Comment placeholders for toggling backends -AC_SUBST(livestatus_enabled) -AC_SUBST(statusdat_enabled) -AC_SUBST(ido_enabled) -AC_SUBST(ldap_enabled) +AC_SUBST(use_ldap_auth) +AC_SUBST(use_internal_auth) # Application and installation AC_SUBST(PHP) @@ -482,7 +439,8 @@ AC_CONFIG_FILES([ config/authentication.ini config/resources.ini config/modules/monitoring/backends.ini - public/index.php + etc/apache/icingaweb.conf + public/.htaccess ]) # diff --git a/doc/CONFIG.md b/doc/CONFIG.md index 5ebaa607b..af7b03bd3 100644 --- a/doc/CONFIG.md +++ b/doc/CONFIG.md @@ -1,5 +1,7 @@ # Application and Module Configuration +## Basic usage + The \Icinga\Application\Config class is a general purpose service to help you find, load and save configuration data. It is used both by the Icinga 2 Web modules and the framework itself. With INI files as source it enables you to store configuration in a familiar format. Icinga 2 Web @@ -22,3 +24,10 @@ keep their main configuration in the INI file called config.ini. Here's some exa // The following example loads values from the example module's extra.ini: IcingaConfig::module('example', 'extra')->logging->get('enabled', true); +## Reload from disk + +If you want to force reading a configuration from disk (i.e. after you modified it), you can use the $fromDisk flag in +the IcingaConfig::app/IcingaConfig::module call: + + IcingaConfig::app('authentication', true)-> ... // read authentication from disk + IcingaConfig::module('example', 'extra', true)->... // read module configuration from disk diff --git a/doc/installation.md b/doc/installation.md index b0633d1e6..177c0aab1 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -8,11 +8,9 @@ If you like to configurea and install icinga2-web from the command line or if you want to create packages, configure and make is the best choice for installation. + ./configure && make install && make install-apache2-config -` -./configure && make install && make install-apache2-config -` -will install the application to the default target (/usr/local/icinga2-web). Also +will install the application to the default target (/usr/local/icinga2-web). Also an apache configuration entry is added to your apache server, so you should restart your web server according to your systems configuration. @@ -20,9 +18,9 @@ your web server according to your systems configuration. If you want to install the application to a different directory, use the --prefix flag in your configure call: -` -./configure --prefix=/my/target/directory -` + + ./configure --prefix=/my/target/directory + ### Authentication diff --git a/doc/test/frontend_tests.md b/doc/test/frontend_tests.md index 9eaa974a3..073dd8c9d 100644 --- a/doc/test/frontend_tests.md +++ b/doc/test/frontend_tests.md @@ -4,37 +4,55 @@ Frontend tests test your code from the users perspective: By opening a specific and expecting something to happen. We use [CasperJS](http://casperjs.org/) for frontend testing, which is basically a headless Webkit browser. -## The current state of frontend testing +**NOTE**: The 1.1.0DEV version does *NOT* work at this time as the api changed. Use the stable 1.0.3 branch instead. -Currently frontend tests are not very advanced: We spawn a small - non php - server on port 12999 to test static files -and javascript behaviour. This will change in the future where we are going to test an installation (or use PHP 5.4 -standalone server). +In order to be able to run the frontend tests, you need a running instance of icingaweb. You should make sure that you +don't need this instance after running the tests, as they could change preferences or configuration ## Writing tests -### Test bootstrap -In order to make testing more comfortable, the i2w config provides a few helpers to make testing more straightforward. -In general you start your test by including i2w-config: +### Test bootstrap - icingawebtest.js module - var i2w = require('./i2w-config'); +The icingawebtest.js module is required for proper testing, as this module eases casperjs usage. After importing the +module with: -and afterward creating a testenvironment with the getTestEnv() method: + var icingawebtest = require('./icingawebtest'); - var casper = i2w.getTestEnv(); +You only need two methods for testing: -You can then start the test by calling casper.start with the startpage (the servers root is always frontend/static, where -public is a symlink to the icingaweb public folder). +* *getTestEnv()*: This method returns a modified casperjs test environment. The difference to then normal casperjs object + is that all methods which take a URL are overloaded so you can add a relative URL if you want to (and + normally you don't want to hardcode your test URLs) + Example: - casper.start("http://localhost:12999/generic.html"); + var casper = icingawebtest.getTestEnv(); -As we use requirejs, this has to be set up for our testcases. i2w provides a setupRequireJs function that does everything for you. -You just have to run this method on your testpage (note that the tested JavaScript is isolated from your test case's JavaScript, if -you want to execute JavaScript you must use the casper.page.evaluate method). +* performLogin(): This calls the login page of your icingaweb instance and tries to login with the supplied credentials - casper.then(function() { - // Setup requirejs - casper.page.evaluate(i2w.setupRequireJs, {icinga: true}); + icinga.performLogin(); + + +Login is performed with the credentials from the CASPERJS_USER/CASPERJS_PASS environment (this can be set with the +./runtest --user %user% --pass %pass% arguments). The host, server and port are also represented as +CASPERJS_HOST, CASPERJS_PORT and CASPERJS_PATH environment settings. The default in runtest resembles the version that +works best in the vagrant development environment: + +* The default user is 'jdoe' +* The default password is 'password' +* The host and port are localhost:80 +* The default path is icinga2-web + +### Writing the test code + +Most tests will require you to login with the supplied credentials, this can be performed with a simple call + + icinga.performLogin(); + +You can then start the test by calling casper.thenOpen with the page you want to work + + casper.thenOpen("/mysite", function() { + // perform tests }); ### Testing diff --git a/etc/apache/cranberry.conf b/etc/apache/cranberry.conf deleted file mode 100644 index 1f6e9878c..000000000 --- a/etc/apache/cranberry.conf +++ /dev/null @@ -1,30 +0,0 @@ -Alias /cranberry /usr/local/i2/public - - SetEnv APPLICATION_ENV development - DirectoryIndex index.php - Options -MultiViews -Indexes +FollowSymLinks - Options SymLinksIfOwnerMatch - AllowOverride AuthConfig FileInfo - Order allow,deny - Allow from all - - RewriteBase /cranberry - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} -s [OR] - RewriteCond %{REQUEST_FILENAME} -l [OR] - RewriteCond %{REQUEST_FILENAME} -d - RewriteRule ^.*$ - [NC,L] - RewriteRule ^.*$ index.php [NC,L] - - - -AliasMatch ^/cranberry/static/([a-zA-Z_\-]+) /usr/local/i2/application/modules/$1/public - - SetEnv APPLICATION_ENV development - Options -MultiViews -Indexes +FollowSymLinks - Options SymLinksIfOwnerMatch - AllowOverride AuthConfig FileInfo - Order allow,deny - Allow from all - - diff --git a/etc/apache/cranberry.conf.in b/etc/apache/cranberry.conf.in deleted file mode 100755 index 2954ad019..000000000 --- a/etc/apache/cranberry.conf.in +++ /dev/null @@ -1,20 +0,0 @@ -Alias @web_path@ @prefix@/public - - DirectoryIndex index.php - Options -MultiViews -Indexes +FollowSymLinks - Options SymLinksIfOwnerMatch - AllowOverride AuthConfig FileInfo - Order allow,deny - Allow from all - - SetEnv APPLICATION_ENV development - RewriteBase @web_path@ - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} -s [OR] - RewriteCond %{REQUEST_FILENAME} -l [OR] - RewriteCond %{REQUEST_FILENAME} -d - RewriteRule ^.*$ - [NC,L] - RewriteRule ^.*$ index.php [NC,L] - - - diff --git a/etc/apache/icingaweb.conf.in b/etc/apache/icingaweb.conf.in new file mode 100755 index 000000000..7c87a2d11 --- /dev/null +++ b/etc/apache/icingaweb.conf.in @@ -0,0 +1,12 @@ +Alias @web_path@ @prefix@/public + + Options -Indexes + + AllowOverride All + + Order allow,deny + Allow from all + + EnableSendfile Off + + diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php index 25e85eada..e5fe14a9e 100644 --- a/library/Icinga/Application/Config.php +++ b/library/Icinga/Application/Config.php @@ -28,7 +28,8 @@ namespace Icinga\Application; -use Zend_Config_Ini; +use \Icinga\Exception\ProgrammingError; +use \Zend_Config_Ini; /** * Global registry of application and module configuration. @@ -37,24 +38,28 @@ class Config extends Zend_Config_Ini { /** * Configuration directory where ALL (application and module) configuration is located + * * @var string */ public static $configDir; /** * The INI file this configuration has been loaded from + * * @var string */ private $configFile; /** * Application config instances per file + * * @var array */ protected static $app = array(); /** * Module config instances per file + * * @var array */ protected static $modules = array(); @@ -62,10 +67,10 @@ class Config extends Zend_Config_Ini /** * Load configuration from the config file $filename * + * @param string $filename The filename to parse + * @see Zend_Config_Ini::__construct - * - * @param string $filename - * @throws Exception + * @throws Exception When the file can't be read */ public function __construct($filename) { @@ -83,12 +88,15 @@ class Config extends Zend_Config_Ini /** * Retrieve a application config instance * - * @param string $configname - * @return mixed + * @param string $configname The configuration name (without ini suffix) to read and return + * @param bool $fromDisk When set true, the configuration will be read from the disk, even + * if it already has been read + * + * @return Config The configuration object that has been requested */ - public static function app($configname = 'config') + public static function app($configname = 'config', $fromDisk = false) { - if (!isset(self::$app[$configname])) { + if (!isset(self::$app[$configname]) || $fromDisk) { $filename = self::$configDir . '/' . $configname . '.ini'; self::$app[$configname] = new Config(realpath($filename)); } @@ -98,17 +106,19 @@ class Config extends Zend_Config_Ini /** * Retrieve a module config instance * - * @param string $modulename - * @param string $configname - * @return Config + * @param string $modulename The name of the module to look for configurations + * @param string $configname The configuration name (without ini suffix) to read and return + * @param string $fromDisk Whether to read the configuration from disk + * + * @return Config The configuration object that has been requested */ - public static function module($modulename, $configname = 'config') + public static function module($modulename, $configname = 'config', $fromDisk = false) { if (!isset(self::$modules[$modulename])) { self::$modules[$modulename] = array(); } $moduleConfigs = self::$modules[$modulename]; - if (!isset($moduleConfigs[$configname])) { + if (!isset($moduleConfigs[$configname]) || $fromDisk) { $filename = self::$configDir . '/modules/' . $modulename . '/' . $configname . '.ini'; if (file_exists($filename)) { $moduleConfigs[$configname] = new Config(realpath($filename)); @@ -136,10 +146,32 @@ class Config extends Zend_Config_Ini } } - public function getConfigFile() + /** + * Return the application wide config file + * + * @return string + */ + public function getConfigFile() { return $this->configFile; } - + /** + * Return the input path with resolved path variables + * + * Currently only %app% is considered a path variable and points to the application paths + * + * @param string $path The path to resolve + * + * @return string The resolved path + */ + public static function resolvePath($path) + { + try { + $appDir = realpath(Icinga::app()->getApplicationDir() . '/..'); + } catch (ProgrammingError $appNotStarted) { + $appDir = realpath(__DIR__ . '/../../..'); + } + return str_replace('{app}', $appDir, $path); + } } diff --git a/library/Icinga/Application/Logger.php b/library/Icinga/Application/Logger.php index d34c6c33a..fd246c26f 100755 --- a/library/Icinga/Application/Logger.php +++ b/library/Icinga/Application/Logger.php @@ -28,6 +28,7 @@ namespace Icinga\Application; +use Icinga\Protocol\Ldap\Exception; use \Zend_Config; use \Zend_Log; use \Zend_Log_Filter_Priority; @@ -144,7 +145,6 @@ final class Logger if ($target == self::DEFAULT_LOG_TARGET) { $type = self::DEFAULT_LOG_TYPE; } - $this->addWriter($type, $target, Zend_Log::DEBUG); } @@ -187,11 +187,17 @@ final class Logger ); return; } - try { - /** @var Zend_Log_Writer_Abstract $writer */ + + $target = Config::resolvePath($target); $writer = new $writerClass($target); $writer->addFilter(new Zend_Log_Filter_Priority($priority)); + // Make sure the permissions for log target file are correct + if ($type === 'Stream' && !file_exists($target)) { + touch($target); + chmod($target, 0664); + } + $this->logger->addWriter($writer); $this->writers[] = $writer; } catch (Zend_Log_Exception $e) { diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index a3c076df8..703f32b3c 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -249,7 +249,8 @@ class Web extends ApplicationBootstrap $preferences->attach($sessionStore); if ($this->getConfig()->preferences !== null) { - if (is_dir($this->getConfig()->preferences->configPath) === false) { + $path = Config::resolvePath($this->getConfig()->preferences->configPath); + if (is_dir($path) === false) { Logger::error( 'Path for preferences not found (IniStore, "%s"). Using default one: "%s"', $this->getConfig()->preferences->configPath, @@ -356,8 +357,6 @@ class Web extends ApplicationBootstrap $view->setView(new View()); $view->view->addHelperPath($this->getApplicationDir('/views/helpers')); - // TODO: find out how to avoid this additional helper path: - $view->view->addHelperPath($this->getApplicationDir('/views/helpers/layout')); $view->view->setEncoding('UTF-8'); $view->view->headTitle()->prepend( diff --git a/library/Icinga/Authentication/Backend/DbUserBackend.php b/library/Icinga/Authentication/Backend/DbUserBackend.php index f75e694b1..149621784 100644 --- a/library/Icinga/Authentication/Backend/DbUserBackend.php +++ b/library/Icinga/Authentication/Backend/DbUserBackend.php @@ -29,7 +29,7 @@ namespace Icinga\Authentication\Backend; -use Zend_Db; +use \Zend_Db; use \Icinga\User; use \Icinga\Authentication\UserBackend; use \Icinga\Authentication\Credentials; @@ -81,9 +81,7 @@ class DbUserBackend implements UserBackend { $this->db = $database; - /* - * Test if the connection is available - */ + // Test if the connection is available $this->db->getConnection(); } @@ -207,4 +205,20 @@ class DbUserBackend implements UserBackend ); return $usr; } + + /** + * Return the number of users in this database connection + * + * This class is mainly used for determining whether the authentication backend is valid or not + * + * @return int The number of users set in this backend + * @see UserBackend::getUserCount + */ + public function getUserCount() + { + + $this->db->getConnection(); + $query = $this->db->select()->from($this->userTable, 'COUNT(*) as count')->query(); + return $query->fetch()->count; + } } diff --git a/library/Icinga/Authentication/Backend/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php index 52fc51704..ac5c3d07f 100644 --- a/library/Icinga/Authentication/Backend/LdapUserBackend.php +++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php @@ -122,12 +122,24 @@ class LdapUserBackend implements UserBackend if (!$this->connection->testCredentials( $this->connection->fetchDN($this->selectUsername($credentials->getUsername())), $credentials->getPassword() - ) - ) { + )) { return false; } $user = new User($credentials->getUsername()); return $user; } + + public function getUserCount() + { + return $this->connection->count( + $this->connection->select()->from( + $this->config->user_class, + array( + $this->config->user_name_attribute + ) + ) + ); + + } } diff --git a/library/Icinga/Authentication/UserBackend.php b/library/Icinga/Authentication/UserBackend.php index ddebb622c..1b8a0b652 100644 --- a/library/Icinga/Authentication/UserBackend.php +++ b/library/Icinga/Authentication/UserBackend.php @@ -28,16 +28,21 @@ namespace Icinga\Authentication; +/** + * Interface for backends that authenticate users + */ interface UserBackend { /** - * Creates a new object + * Create a userbackend from the given configuration or resource + * * @param $config */ public function __construct($config); /** * Test if the username exists + * * @param Credentials $credentials * @return boolean */ @@ -45,8 +50,16 @@ interface UserBackend /** * Authenticate + * * @param Credentials $credentials * @return User */ public function authenticate(Credentials $credentials); + + /** + * Get the number of users available through this backend + * + * @return int + */ + public function getUserCount(); } diff --git a/library/Icinga/User/Preferences/IniStore.php b/library/Icinga/User/Preferences/IniStore.php index 823c8b07e..0bd98ec63 100644 --- a/library/Icinga/User/Preferences/IniStore.php +++ b/library/Icinga/User/Preferences/IniStore.php @@ -28,13 +28,16 @@ namespace Icinga\User\Preferences; +use Icinga\Application\Logger; +use Icinga\Protocol\Ldap\Exception; use \SplObserver; use \SplSubject; -use Icinga\User; -use Icinga\User\Preferences; -use Icinga\Exception\ConfigurationError; -use Icinga\Exception\ProgrammingError; +use \Icinga\User; +use \Icinga\User\Preferences; +use \Icinga\Exception\ConfigurationError; +use \Icinga\Exception\ProgrammingError; use \Zend_Config; +use \Icinga\Application\Config as IcingaConfig; use \Zend_Config_Writer_Ini; /** @@ -84,7 +87,7 @@ class IniStore implements LoadInterface, FlushObserverInterface * * @param string|null $configPath */ - public function __construct($configPath=null) + public function __construct($configPath = null) { if ($configPath !== null) { $this->setConfigPath($configPath); @@ -99,6 +102,7 @@ class IniStore implements LoadInterface, FlushObserverInterface */ public function setConfigPath($configPath) { + $configPath = IcingaConfig::resolvePath($configPath); if (!is_dir($configPath)) { throw new ConfigurationError('Config dir dos not exist: '. $configPath); } @@ -124,18 +128,22 @@ class IniStore implements LoadInterface, FlushObserverInterface if (file_exists($this->preferencesFile) === false) { $this->createDefaultIniFile(); } + try { + $this->iniConfig = new Zend_Config( + parse_ini_file($this->preferencesFile), + true + ); - $this->iniConfig = new Zend_Config( - parse_ini_file($this->preferencesFile), - true - ); - - $this->iniWriter = new Zend_Config_Writer_Ini( - array( - 'config' => $this->iniConfig, - 'filename' => $this->preferencesFile - ) - ); + $this->iniWriter = new Zend_Config_Writer_Ini( + array( + 'config' => $this->iniConfig, + 'filename' => $this->preferencesFile + ) + ); + } catch (Exception $e) { + Logger::error('Could not create IniStore backend: %s', $e->getMessage()); + throw new \RuntimeException("Creating user preference backend failed"); + } } /** @@ -144,6 +152,8 @@ class IniStore implements LoadInterface, FlushObserverInterface private function createDefaultIniFile() { touch($this->preferencesFile); + chmod($this->preferencesFile, 0664); + } /** diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index d3d8192fd..75aedb36b 100755 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -230,4 +230,25 @@ class ActionController extends ZfController } } } + + /** + * Try to call compatible methods from older zend versions + * + * Methods like getParam and redirect are _getParam/_redirect in older Zend versions (which reside for example + * in Debian Wheezy). Using those methods without the "_" causes the application to fail on those platforms, but + * using the version with "_" forces us to use deprecated code. So we try to catch this issue by looking for methods + * with the same name, but with a "_" prefix prepended. + * + * @param string $name The method name to check + * @param array $params The method parameters + */ + public function __call($name, $params) + { + $deprecatedMethod = '_'.$name; + + if (method_exists($this, $deprecatedMethod)) { + return call_user_func_array(array($this, $deprecatedMethod), $params); + } + return parent::__call($name, $params); + } } diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index b530f3a4a..eddb70e88 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -38,7 +38,7 @@ use \Icinga\Web\Form\Element\Note; /** * Base class for forms providing CSRF protection, confirmation logic and auto submission */ -abstract class Form extends Zend_Form +class Form extends Zend_Form { /** * The form's request object @@ -161,7 +161,9 @@ abstract class Form extends Zend_Form /** * Add elements to this form (used by extending classes) */ - abstract protected function create(); + protected function create() + { + } /** * Method called before validation @@ -322,7 +324,9 @@ abstract class Form extends Zend_Form * Ensures that the current request method is POST, that the form was manually submitted and that the data provided * in the request is valid and gets repopulated in case its invalid. * - * @return bool + * @return bool True when the form is submitted and valid, otherwise false + * @see Form::isValid() + * @see Form::isSubmitted() */ public function isSubmittedAndValid() { @@ -334,12 +338,7 @@ abstract class Form extends Zend_Form $checkData = $this->getRequest()->getParams(); $this->assertValidCsrfToken($checkData); - $submitted = true; - if ($this->submitLabel) { - $submitted = isset($checkData['btn_submit']); - } - - if ($submitted) { + if ($this->isSubmitted()) { // perform full validation if submitted $this->preValidation($checkData); return $this->isValid($checkData); @@ -350,6 +349,24 @@ abstract class Form extends Zend_Form } } + /** + * Check whether this form has been submitted + * + * Per default, this checks whether the button set with the 'setSubmitLabel' method + * is being submitted. For custom submission logic, this method must be overwritten + * + * @return bool True when the form is marked as submitted, otherwise false + */ + public function isSubmitted() + { + $submitted = true; + if ($this->submitLabel) { + $checkData = $this->getRequest()->getParams(); + $submitted = isset($checkData['btn_submit']); + } + return $submitted; + } + /** * Disable CSRF counter measure and remove its field if already added * diff --git a/library/Icinga/Web/Form/Validator/WritablePathValidator.php b/library/Icinga/Web/Form/Validator/WritablePathValidator.php index 0447a4f42..573d7f529 100644 --- a/library/Icinga/Web/Form/Validator/WritablePathValidator.php +++ b/library/Icinga/Web/Form/Validator/WritablePathValidator.php @@ -29,6 +29,7 @@ namespace Icinga\Web\Form\Validator; use \Zend_Validate_Abstract; +use \Icinga\Application\Config as IcingaConfig; /** * Validator that interprets the value as a path and checks if it's writable @@ -77,8 +78,9 @@ class WritablePathValidator extends Zend_Validate_Abstract public function isValid($value, $context = null) { $value = (string) $value; - $this->_setValue($value); + $this->_setValue($value); + $value = IcingaConfig::resolvePath($value); if ($this->requireExistence && !file_exists($value)) { $this->_error('DOES_NOT_EXIST'); return false; diff --git a/modules/monitoring/application/controllers/ConfigController.php b/modules/monitoring/application/controllers/ConfigController.php index a0777ee46..76b12df08 100644 --- a/modules/monitoring/application/controllers/ConfigController.php +++ b/modules/monitoring/application/controllers/ConfigController.php @@ -70,6 +70,7 @@ class Monitoring_ConfigController extends BaseConfigController { { $this->view->backends = IcingaConfig::module('monitoring', 'backends')->toArray(); $this->view->instances = IcingaConfig::module('monitoring', 'instances')->toArray(); + $this->render('index'); } /** @@ -114,11 +115,12 @@ class Monitoring_ConfigController extends BaseConfigController { $configArray[$form->getBackendName()] = $form->getConfig(); if ($this->writeConfiguration(new Zend_Config($configArray), 'backends')) { - $this->redirectNow('monitoring/config'); + $this->view->successMessage = 'Backend Creation Succeeded'; + $this->indexAction(); } else { $this->render('show-configuration'); - return; } + return; } $this->view->form = $form; $this->render('editbackend'); @@ -143,11 +145,12 @@ class Monitoring_ConfigController extends BaseConfigController { unset($configArray[$backend]); if ($this->writeConfiguration(new Zend_Config($configArray), 'backends')) { - $this->redirectNow('monitoring/config'); + $this->view->successMessage = 'Backend "' . $backend . '" Removed'; + $this->indexAction(); } else { $this->render('show-configuration'); - return; } + return; } $this->view->form = $form; @@ -174,11 +177,12 @@ class Monitoring_ConfigController extends BaseConfigController { unset($configArray[$instance]); if ($this->writeConfiguration(new Zend_Config($configArray), 'instances')) { - $this->redirectNow('monitoring/config'); + $this->view->successMessage = 'Instance "' . $instance . '" Removed'; + $this->indexAction(); } else { $this->render('show-configuration'); - return; } + return; } $this->view->form = $form; @@ -202,7 +206,8 @@ class Monitoring_ConfigController extends BaseConfigController { $instanceConfig = IcingaConfig::module('monitoring', 'instances')->toArray(); $instanceConfig[$instance] = $form->getConfig(); if ($this->writeConfiguration(new Zend_Config($instanceConfig), 'instances')) { - $this->redirectNow('monitoring/config'); + $this->view->successMessage = 'Instance Modified'; + $this->indexAction(); } else { $this->render('show-configuration'); return; @@ -222,11 +227,12 @@ class Monitoring_ConfigController extends BaseConfigController { $instanceConfig = IcingaConfig::module('monitoring', 'instances')->toArray(); $instanceConfig[$form->getInstanceName()] = $form->getConfig()->toArray(); if ($this->writeConfiguration(new Zend_Config($instanceConfig), 'instances')) { - $this->redirectNow('monitoring/config'); + $this->view->successMessage = 'Instance Creation Succeeded'; + $this->indexAction(); } else { $this->render('show-configuration'); - return; } + return; } $this->view->form = $form; $this->render('editinstance'); diff --git a/modules/monitoring/application/views/scripts/config/index.phtml b/modules/monitoring/application/views/scripts/config/index.phtml index f256be33b..a12501f9f 100644 --- a/modules/monitoring/application/views/scripts/config/index.phtml +++ b/modules/monitoring/application/views/scripts/config/index.phtml @@ -3,6 +3,13 @@

Monitoring Backends

+successMessage): ?> +
+ {{OK_ICON}} + escape($this->successMessage); ?> +
+ +
{{CREATE_ICON}} Create New Monitoring Backend @@ -16,9 +23,6 @@ $backendName)); ?> escape($backendName); ?> (Type: escape($config['type'] === 'ido' ? 'IDO' : ucfirst($config['type'])); ?>) - - - {{DISABLED_ICON}} Disabled -
{{REMOVE_ICON}} Remove This Backend
{{EDIT_ICON}} Edit This Backend diff --git a/public/.htaccess b/public/.htaccess index 6adf83a17..fd6edea2b 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -1,6 +1,7 @@ SetEnv APPLICATION_ENV development RewriteEngine on +RewriteBase /icinga2-web RewriteRule ^css/icinga.css css.php RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] diff --git a/public/.htaccess.in b/public/.htaccess.in new file mode 100644 index 000000000..c6536f00d --- /dev/null +++ b/public/.htaccess.in @@ -0,0 +1,14 @@ +SetEnv APPLICATION_ENV development + +RewriteEngine on +RewriteBase @web_path@ +RewriteRule ^css/icinga.css css.php +RewriteCond %{REQUEST_FILENAME} -s [OR] +RewriteCond %{REQUEST_FILENAME} -l [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^.*$ - [NC,L] +RewriteRule ^.*$ index.php [NC,L] + +php_flag short_open_tag on + +php_value xdebug.idekey PHPSTORM diff --git a/public/index.php.in b/public/index.php.in deleted file mode 100644 index 49753a1a5..000000000 --- a/public/index.php.in +++ /dev/null @@ -1,10 +0,0 @@ -dispatch(); diff --git a/test/frontend/cases/historyApiTest.js b/test/frontend/cases/historyApiTest.js deleted file mode 100644 index a346447a9..000000000 --- a/test/frontend/cases/historyApiTest.js +++ /dev/null @@ -1,61 +0,0 @@ - -var i2w = require('./i2w-config'); -var casper = i2w.getTestEnv(); - -casper.start("http://localhost:12999/generic.html"); - -casper.then(function() { - casper.page.evaluate(i2w.setupRequireJs, {icinga: true}); -}); - - -casper.then(function() { - this.test.assertTitle("Icinga test page"); - casper.page.evaluate(function() { - requirejs(["icinga/icinga"], function(icinga) { - icinga.loadUrl("/fragments/testFragment1.html"); - }); - }); - /*this.waitFor(function() { - return document.querySelectorAll("#icinga-main a") ; - }, */ - casper.waitForSelector("div#icinga-main a", onFirstLink); -}); - -var onFirstLink = function() { - var links = casper.page.evaluate(function() { - return document.querySelectorAll("div#icinga-main a"); - }); - // assert no reload - this.test.assertTitle("Icinga test page"); - this.test.assertUrlMatch(/.*testFragment1.html/); - this.test.assertEquals(links.length, 2); - casper.clickLabel('Fragment 2'); - casper.waitForText('Fragment 1', onSecondLink); -}; - -var onSecondLink = function() { - var links = casper.page.evaluate(function() { - return document.querySelectorAll("div#icinga-main a"); - }); - this.test.assertTitle("Icinga test page"); - this.test.assertUrlMatch(/.*testFragment2.html/); - this.test.assertEquals(links.length, 2); - casper.page.evaluate(function() { - requirejs(["icinga/icinga"], function(icinga) { - icinga.loadUrl("/fragments/testFragment3.html?this=is_a_param", "icinga-detail"); - - }); - }); - this.wait(400, function() { - console.log(casper.page.evaluate(function() { - return window.location.href; - })); - - this.test.assertUrlMatch(/testFragment2.html.*testFragment3.html/); - }); -}; - -casper.run(function() { - this.test.done(); -}); diff --git a/test/frontend/cases/loginpageTest.js b/test/frontend/cases/loginpageTest.js new file mode 100644 index 000000000..21b800fd7 --- /dev/null +++ b/test/frontend/cases/loginpageTest.js @@ -0,0 +1,175 @@ +/** + * Test case for the login page + * + * Steps: + * - Request application root path + * - Assert login page to appear + * - Enter invalid credentials + * - Enter valid credentials + * - Reload page without credentials + * - Logout + **/ + +/** + * The icinga util object + * + * @type object + */ +var icinga = require('./icingawebtest'); + +/** + * The casperjs object + * + * @type Casper + */ +var casper = icinga.getTestEnv(); + +/** + * Test whether the login form exists and has valid input elements + * + * @param {testing} The casperjs testing module to perform assertions + */ +var assertLoginFormExists = function(test) { + + test.assertExists( + 'form#form_login', + 'Test whether the login form exists' + ); + test.assertExists( + 'form#form_login input#username', + 'Test whether a username input field exists' + ); + test.assertExists( + 'form#form_login input#password', + 'Test whether a password input field exists' + ); + test.assertExists( + 'form#form_login input#submit', + 'Test whether a submit input field exists' + ); +}; + +/** + * Request the initial application path + */ +casper.start('/', function() { + if (this.getCurrentUrl() === 'about:blank') { + this.die('Url can\'t be accessed'); + } + this.test.assertTitle( + "Icinga Web Login", + "Test whether the login page (" + this.getCurrentUrl() + ") has a correct title" + ); + assertLoginFormExists(this.test); + this.test.assertDoesntExist( + '#icinga_app_username', + 'Test if no username is set in the frontend after initial page load' + ); +}); + +/** + * Login with invalid credentials + */ +casper.then(function() { + this.fill('form#form_login', { + 'username' : 'no', + 'password' : 'existing_user' + }); + this.click('form#form_login input#submit'); +}); + +/** + * Test if login failed and feedback is given + */ +casper.then(function() { + this.test.assertTextExists( + 'Please provide a valid username and password', + 'Test if the user gets a note that authorization failed if providing wrong credentials' + ); + assertLoginFormExists(this.test); + this.test.assertDoesntExist( + '#icinga_app_username', + 'Test if no username is set in the frontend after entering wrong credentials' + ); + +}); + +/** + * Login with valid credentials + */ +casper.then(function() { + this.fill('form#form_login', icinga.getCredentials()); + this.click('form#form_login input#submit'); +}); + +/** + * Test if the login suceeded and the username is shown in the navigation bar + */ +casper.then(function() { + this.test.assertTextDoesntExist( + 'Please provide a valid username and password', + 'Test if valid credentials don\'t end cause a note that credentials are wrong to appear' + ); + this.test.assertSelectorHasText( + '#icinga_app_nav_username', + icinga.getCredentials().username, + 'Test if the username is set in the frontend after successful login' + ); +}); + +/** + * Test if session is persisted after reloading the page + */ +casper.thenOpen('/', function() { + this.test.assertSelectorHasText( + '#icinga_app_nav_username', + icinga.getCredentials().username, + 'Test if the username is still set if reloading the page via GET' + ); + + this.test.assertExists( + '#icinga_app_nav_logout', + 'Test if the logout button exists' + ); + + this.test.assertExists( + '#icinga_app_nav_useraction', + 'Test whether the dropdown for user specific actions exists' + ); +}); + +/** + * Test if logout button is displayed when username is clicked and test for correct logout + */ +casper.then(function() { + this.test.assertNotVisible( + '#icinga_app_nav_logout', + 'Test if the logout button is hidden when not clicked' + ); + + this.wait(500, function() { // wait until everything is initialized, sometimes this takes a while + this.click('#icinga_app_nav_useraction'); + this.waitUntilVisible('#icinga_app_nav_logout', function() { + this.click('#icinga_app_nav_logout a'); + this.waitForSelector('form#form_login', function() { + this.test.assertDoesntExist( + '#icinga_app_username', + 'Test if no username is set in the frontend after logout' + ); + assertLoginFormExists(this.test); + }); + }, function() { + this.test.assertVisible( + '#icinga_app_nav_logout', + 'Test if the logout button is visible when click on username occurs' + ); + }, 500); + }); +}); + +/** + * Run the tests + */ +casper.run(function() { + this.test.done(); +}); diff --git a/test/frontend/cases/static-page-test.js b/test/frontend/cases/static-page-test.js deleted file mode 100644 index 39f94081f..000000000 --- a/test/frontend/cases/static-page-test.js +++ /dev/null @@ -1,21 +0,0 @@ -/** -* -* This test simply checks the icinga build server and tests -* if the title is correct -**/ -i2w = require('./i2w-config'); - -var casper = i2w.getTestEnv(); - -casper.start("http://localhost:12999/empty.html"); - - -casper.then(function() { - this.test.assertTitle("Just an empty page"); -}); - - -casper.run(function() { - this.test.done(); -}); - diff --git a/test/frontend/casperjs.config b/test/frontend/casperjs.config deleted file mode 100644 index 69c9d4a3b..000000000 --- a/test/frontend/casperjs.config +++ /dev/null @@ -1,5 +0,0 @@ -{ - "host": "localhost", - "port": 80, - "path": "icinga2-web" -} diff --git a/test/frontend/i2w-config.js b/test/frontend/i2w-config.js deleted file mode 100644 index 4d72b6d7e..000000000 --- a/test/frontend/i2w-config.js +++ /dev/null @@ -1,158 +0,0 @@ -/** -* Tools for setting up the casperjs tests -* mainly setting host, port and path path -**/ - -// load config files -var fs = require('fs'); -var env = require('system').env; -var args = require('system').args; -var utils = require('utils'); - - -var configFile = fs.absolute('./casperjs.config'); -var host = null; -var port = null; -var path = null; -var verbose = false; - - -if (typeof(env.CASPERJS_HOST) === "string") - host = env.CASPERJS_HOST; -if (typeof(env.CASPERJS_PORT) === "string") - port = parseInt(env.CASPERJS_PORT, 10); -if (typeof(env.CASPERJS_PATH) === "string") - path = env.CASPERJS_PATH; - - -for (var i=0;i - - Just an empty page - - - - diff --git a/test/frontend/static/fragments/testFragment1.html b/test/frontend/static/fragments/testFragment1.html deleted file mode 100644 index 2d6ef70db..000000000 --- a/test/frontend/static/fragments/testFragment1.html +++ /dev/null @@ -1,6 +0,0 @@ -
-

Test fragment

- Fragment 2 - Fragment 3 - -
diff --git a/test/frontend/static/fragments/testFragment2.html b/test/frontend/static/fragments/testFragment2.html deleted file mode 100644 index 4db44e053..000000000 --- a/test/frontend/static/fragments/testFragment2.html +++ /dev/null @@ -1,6 +0,0 @@ -
-

Test fragment 2

- Fragment 1 - Fragment 3 - -
diff --git a/test/frontend/static/fragments/testFragment3.html b/test/frontend/static/fragments/testFragment3.html deleted file mode 100644 index e773f5eff..000000000 --- a/test/frontend/static/fragments/testFragment3.html +++ /dev/null @@ -1,6 +0,0 @@ -
-

Test fragment 3

- Fragment 1 - Fragment 2 - -
diff --git a/test/frontend/static/generic.html b/test/frontend/static/generic.html deleted file mode 100644 index d1e8c301a..000000000 --- a/test/frontend/static/generic.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - Icinga test page - - - - - - - - - - - - - -
-
-
- -
- - - diff --git a/test/frontend/static/moduleMock.js b/test/frontend/static/moduleMock.js deleted file mode 100644 index aa9c4079f..000000000 --- a/test/frontend/static/moduleMock.js +++ /dev/null @@ -1,4 +0,0 @@ -define([], function() { - "use strict"; - return {}; -}); diff --git a/test/frontend/test.xml b/test/frontend/test.xml deleted file mode 100644 index 68ba1bc3a..000000000 --- a/test/frontend/test.xml +++ /dev/null @@ -1 +0,0 @@ -Page title is: "i4cinga-web test [Jenkins]" \ No newline at end of file diff --git a/test/php/application/forms/Config/AuthenticationFormTest.php b/test/php/application/forms/Config/AuthenticationFormTest.php index 92745576a..8dc104035 100644 --- a/test/php/application/forms/Config/AuthenticationFormTest.php +++ b/test/php/application/forms/Config/AuthenticationFormTest.php @@ -38,17 +38,21 @@ use Icinga\Test\BaseTestCase; require_once 'Zend/Form.php'; require_once 'Zend/Config.php'; require_once 'Zend/Config/Ini.php'; + +require_once BaseTestCase::$testDir . '/library/Icinga/Web/RequestMock.php'; + require_once BaseTestCase::$libDir . '/Web/Form.php'; -require_once BaseTestCase::$appDir . '/forms/Config/AuthenticationForm.php'; +require_once BaseTestCase::$libDir . '/Web/Url.php'; + require_once BaseTestCase::$appDir . '/forms/Config/Authentication/BaseBackendForm.php'; require_once BaseTestCase::$appDir . '/forms/Config/Authentication/DbBackendForm.php'; require_once BaseTestCase::$appDir . '/forms/Config/Authentication/LdapBackendForm.php'; // @codingStandardsIgnoreEnd use \Icinga\Web\Form; -use \DOMDocument; +use Icinga\Web\Url; +use Tests\Icinga\Web\RequestMock; use \Zend_Config; -use \Zend_View; /** * Test for the authentication provider form @@ -92,27 +96,26 @@ class AuthenticationFormTest extends BaseTestCase public function testLdapProvider() { $this->requireFormLibraries(); - $form = $this->createForm('Icinga\Form\Config\AuthenticationForm'); + $form = $this->createForm('Icinga\Form\Config\Authentication\LdapBackendForm'); $config = new Zend_Config( array( - 'test-ldap' => array( - 'backend' => 'ldap', - 'target' => 'user', - 'hostname' => 'test host', - 'root_dn' => 'ou=test,dc=icinga,dc=org', - 'bind_dn' => 'cn=testuser,cn=config', - 'bind_pw' => 'password', - 'user_class' => 'testClass', - 'user_name_attribute' => 'testAttribute' - ) + 'backend' => 'ldap', + 'target' => 'user', + 'hostname' => 'test host', + 'root_dn' => 'ou=test,dc=icinga,dc=org', + 'bind_dn' => 'cn=testuser,cn=config', + 'bind_pw' => 'password', + 'user_class' => 'testClass', + 'user_name_attribute' => 'testAttribute' ) ); - $form->setConfiguration($config); + $form->setBackendName('testldap'); + $form->setBackend($config); $form->create(); // parameters to be hidden $notShown = array('backend', 'target'); - foreach ($config->get('test-ldap')->toArray() as $name => $value) { + foreach ($config->toArray() as $name => $value) { if (in_array($name, $notShown)) { continue; } @@ -123,22 +126,19 @@ class AuthenticationFormTest extends BaseTestCase ); } } - +/* /** * Test the database provider form population from config - * */ public function testDbProvider() { $this->requireFormLibraries(); - $form = $this->createForm('Icinga\Form\Config\AuthenticationForm'); + $form = $this->createForm('Icinga\Form\Config\Authentication\DbBackendForm'); $config = new Zend_Config( array( - 'test-db' => array( - 'backend' => 'db', - 'target' => 'user', - 'resource' => 'db_resource' - ) + 'backend' => 'db', + 'target' => 'user', + 'resource' => 'db_resource' ) ); $form->setResources( @@ -149,12 +149,13 @@ class AuthenticationFormTest extends BaseTestCase ) ); - $form->setConfiguration($config); + $form->setBackendName('test-db'); + $form->setBackend($config); $form->create(); // parameters to be hidden $notShown = array('backend', 'target'); - foreach ($config->get('test-db')->toArray() as $name => $value) { + foreach ($config->toArray() as $name => $value) { if (in_array($name, $notShown)) { continue; } @@ -169,171 +170,70 @@ class AuthenticationFormTest extends BaseTestCase /** * Test whether order modifications via 'priority' are considered * + * @backupStaticAttributes enabled */ - public function testShowModifiedOrder() + public function testModifyOrder() { + Url::$overwrittenRequest = new RequestMock(); $this->requireFormLibraries(); - $form = $this->createForm( - 'Icinga\Form\Config\AuthenticationForm', - array( - 'priority' => 'test-ldap,test-db' - ) - ); - $config = $this->getTestConfig(); - $form->setResources( - array( - 'db_resource' => array( - 'type' => 'db' - ) - ) - ); + $form = $this->createForm('Icinga\Form\Config\Authentication\ReorderForm'); + $form->setAuthenticationBackend('backend2'); + $form->setCurrentOrder(array('backend1', 'backend2', 'backend3', 'backend4')); - $form->setConfiguration($config); $form->create(); + $this->assertSame( + 2, + count($form->getSubForms()), + 'Assert that a form for moving backend up and down exists' + ); + $this->assertTrue( + $form->upForm->getElement('form_backend_order') !== null, + 'Assert that a "move backend up" button exists' + ); + $this->assertSame( + array('backend2', 'backend1', 'backend3', 'backend4'), + explode(',', $form->upForm->getElement('form_backend_order')->getValue()), + 'Assert the "move backend up" button containing the correct order' + ); - $prio = array_keys($form->getConfig()); - $this->assertEquals('test-ldap', $prio[0], "Asserting priority changes to be persisted"); - $this->assertEquals('test-db', $prio[1], "Asserting priority changes to be persisted"); + $this->assertTrue( + $form->downForm->getElement('form_backend_order') !== null, + 'Assert that a "move backend down" button exists' + ); + $this->assertSame( + array('backend1', 'backend3', 'backend2', 'backend4'), + explode(',', $form->downForm->getElement('form_backend_order')->getValue()), + 'Assert the "move backend up" button containing the correct order' + ); } /** - * Test whether configuration changes are correctly returned when calling getConfig + * Test whether the reorder form doesn't display senseless ordering (like moving the uppermost element up or + * the lowermose down) * + * @backupStaticAttributes enabled */ - public function testConfigurationCreation() + public function testInvalidOrderingNotShown() { + Url::$overwrittenRequest = new RequestMock(); $this->requireFormLibraries(); - $form = $this->createForm( - 'Icinga\Form\Config\AuthenticationForm', - array( - 'priority' => 'test-ldap,test-db', - 'backend_testdb_resource' => 'db_resource_2', - 'backend_testldap_hostname' => 'modified_host', - 'backend_testldap_root_dn' => 'modified_root_dn', - 'backend_testldap_bind_dn' => 'modified_bind_dn', - 'backend_testldap_bind_pw' => 'modified_bind_pw', - 'backend_testldap_user_class' => 'modified_user_class', - 'backend_testldap_user_name_attribute' => 'modified_user_name_attribute' - ) - ); + $form = $this->createForm('Icinga\Form\Config\Authentication\ReorderForm'); + $form->setAuthenticationBackend('backend1'); + $form->setCurrentOrder(array('backend1', 'backend2', 'backend3', 'backend4')); - $form->setResources( - array( - 'db_resource' => array( - 'type' => 'db' - ), - 'db_resource_2' => array( - 'type' => 'db' - ) - ) - ); - - $form->setConfiguration($this->getTestConfig()); $form->create(); - - $modified = new Zend_Config($form->getConfig()); - $this->assertEquals( - 'db_resource_2', - $modified->get('test-db')->resource, - 'Asserting database resource modifications to be applied' + $this->assertSame( + 2, + count($form->getSubForms()), + 'Assert that a form for moving backend up and down exists, even when moving up is not possible' ); - $this->assertEquals( - 'user', - $modified->get('test-db')->target, - 'Asserting database target still being user when modifying' + $this->assertTrue( + $form->downForm->getElement('form_backend_order') !== null, + 'Assert that a "move backend down" button exists when moving up is not possible' ); - $this->assertEquals( - 'db', - $modified->get('test-db')->backend, - 'Asserting database backend still being db when modifying' + $this->assertTrue( + $form->upForm->getElement('form_backend_order') === null, + 'Assert that a "move backend up" button does not exist when moving up is not possible' ); - - $ldap = $modified->get('test-ldap'); - $this->assertEquals( - 'modified_host', - $ldap->hostname, - 'Asserting hostname modifications to be applied when modifying ldap authentication backends' - ); - - $this->assertEquals( - 'modified_root_dn', - $ldap->root_dn, - 'Asserting root dn modifications to be applied when modifying ldap authentication backends' - ); - - $this->assertEquals( - 'modified_bind_dn', - $ldap->bind_dn, - 'Asserting bind dn modifications to be applied when modifying ldap authentication backends' - ); - - $this->assertEquals( - 'modified_bind_pw', - $ldap->bind_pw, - 'Asserting bind pw modifications to be applied when modifying ldap authentication backends' - ); - - $this->assertEquals( - 'modified_user_class', - $ldap->user_class, - 'Asserting user class modifications to be applied when modifying ldap authentication backends' - ); - - $this->assertEquals( - 'modified_user_name_attribute', - $ldap->user_name_attribute, - 'Asserting user name attribute modifications to be applied when modifying ldap authentication backends' - ); - } - - /** - * Test correct behaviour when ticking the 'remove backend' option - */ - public function testBackendRemoval() - { - $this->requireFormLibraries(); - $form = $this->createForm( - 'Icinga\Form\Config\AuthenticationForm', - array( - 'priority' => 'test-ldap,test-db', - 'backend_testdb_resource' => 'db_resource_2', - 'backend_testldap_remove' => 1, - 'backend_testldap_hostname' => 'modified_host', - 'backend_testldap_root_dn' => 'modified_root_dn', - 'backend_testldap_bind_dn' => 'modified_bind_dn', - 'backend_testldap_bind_pw' => 'modified_bind_pw', - 'backend_testldap_user_class' => 'modified_user_class', - 'backend_testldap_user_name_attribute' => 'modified_user_name_attribute' - ) - ); - - $form->setResources( - array( - 'db_resource' => array( - 'type' => 'db' - ), - 'db_resource_2' => array( - 'type' => 'db' - ) - ) - ); - - $form->setConfiguration($this->getTestConfig()); - $form->create(); - $view = new Zend_View(); - - $html = new DOMDocument(); - $html->loadHTML($form->render($view)); - $this->assertEquals( - null, - $html->getElementById('backend_testldap_hostname-element'), - 'Asserting configuration to be hidden when an authentication is marked as to be removed' - ); - $config = $form->getConfig(); - $this->assertFalse( - isset($config['test-ldap']), - 'Asserting deleted backends not being persisted in the configuration' - ); - } } diff --git a/test/php/application/forms/Preference/GeneralFormTest.php b/test/php/application/forms/Preference/GeneralFormTest.php index 1299ae856..a12c50f88 100644 --- a/test/php/application/forms/Preference/GeneralFormTest.php +++ b/test/php/application/forms/Preference/GeneralFormTest.php @@ -87,7 +87,7 @@ class GeneralFormTest extends BaseTestCase * Test whether fields with preferences are enabled * */ - public function testEnsableFormIfUsingPreference() + public function testEnableFormIfUsingPreference() { $this->requireFormLibraries(); $form = $this->createForm('Icinga\Form\Preference\GeneralForm'); diff --git a/test/php/library/Icinga/Authentication/BackendMock.php b/test/php/library/Icinga/Authentication/BackendMock.php index 9249d5729..786e5757f 100644 --- a/test/php/library/Icinga/Authentication/BackendMock.php +++ b/test/php/library/Icinga/Authentication/BackendMock.php @@ -49,7 +49,11 @@ class BackendMock implements UserBackend "user@test.local" ); } - + + public function getUserCount() { + return count($this->allowedCredentials); + } + public function authenticate(Credentials $credentials) { if (!in_array($credentials, $this->allowedCredentials)) { diff --git a/test/php/library/Icinga/Web/Form/Validator/WritablePathValidatorTest.php b/test/php/library/Icinga/Web/Form/Validator/WritablePathValidatorTest.php index b9b579b29..c4d9b50bb 100644 --- a/test/php/library/Icinga/Web/Form/Validator/WritablePathValidatorTest.php +++ b/test/php/library/Icinga/Web/Form/Validator/WritablePathValidatorTest.php @@ -30,6 +30,7 @@ namespace Test\Icinga\Web\Form\Validator; require_once('Zend/Validate/Abstract.php'); require_once(realpath('../../library/Icinga/Web/Form/Validator/WritablePathValidator.php')); +require_once(realpath('../../library/Icinga/Application/Config.php')); use \PHPUnit_Framework_TestCase; use \Icinga\Web\Form\Validator\WritablePathValidator;