From 17e6402aa9691f2665089fb2c2866157f5d12206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Mon, 26 Aug 2013 16:56:23 +0200 Subject: [PATCH] Split up authentication form, logic connectivity check refs #4606 refs #4622 refs #4602 refs #4546 --- application/controllers/ConfigController.php | 143 ++++++- .../Config/Authentication/BaseBackendForm.php | 52 +++ .../Config/Authentication/DbBackendForm.php | 6 + .../Config/Authentication/LdapBackendForm.php | 28 ++ .../Config/Authentication/ReorderForm.php | 266 ++++++++++++ .../forms/Config/AuthenticationForm.php | 385 ------------------ .../forms/Config/ConfirmRemovalForm.php | 82 ++++ .../views/scripts/config/authentication.phtml | 73 ++-- .../config/authentication/create.phtml | 15 + .../config/authentication/modify.phtml | 12 +- .../config/authentication/remove.phtml | 4 + library/Icinga/Application/Config.php | 21 +- library/Icinga/Application/Web.php | 2 - .../Backend/LdapUserBackend.php | 18 +- library/Icinga/Web/Form.php | 34 +- 15 files changed, 690 insertions(+), 451 deletions(-) create mode 100644 application/forms/Config/Authentication/ReorderForm.php delete mode 100644 application/forms/Config/AuthenticationForm.php create mode 100644 application/forms/Config/ConfirmRemovalForm.php create mode 100644 application/views/scripts/config/authentication/create.phtml create mode 100644 application/views/scripts/config/authentication/remove.phtml diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index b90fcd055..3c939ac9c 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -33,10 +33,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 +45,7 @@ use \Icinga\Config\PreservingIniWriter; */ class ConfigController extends BaseConfigController { + /** * Create tabs for this configuration controller * @@ -97,7 +99,7 @@ class ConfigController extends BaseConfigController $form->setRequest($this->_request); if ($form->isSubmittedAndValid()) { if (!$this->writeConfigFile($form->getConfig(), 'config')) { - return false; + return; } $this->redirectNow('/config'); } @@ -158,24 +160,69 @@ class ConfigController extends BaseConfigController /** * 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 +235,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/forms/Config/Authentication/BaseBackendForm.php b/application/forms/Config/Authentication/BaseBackendForm.php index 01b0acb43..2b913d9d9 100644 --- a/application/forms/Config/Authentication/BaseBackendForm.php +++ b/application/forms/Config/Authentication/BaseBackendForm.php @@ -129,6 +129,48 @@ abstract class BaseBackendForm extends Form } } + /** + * Add a forcebox 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 mechanismn 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->validateAuthenticationBackend()) { + $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 +178,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 validateAuthenticationBackend(); } diff --git a/application/forms/Config/Authentication/DbBackendForm.php b/application/forms/Config/Authentication/DbBackendForm.php index 3c09bccf6..6bb79eecb 100644 --- a/application/forms/Config/Authentication/DbBackendForm.php +++ b/application/forms/Config/Authentication/DbBackendForm.php @@ -29,6 +29,7 @@ namespace Icinga\Form\Config\Authentication; +use Icinga\Authentication\Backend\DbUserBackend; use \Zend_Config; use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; @@ -118,4 +119,9 @@ class DbBackendForm extends BaseBackendForm $section => $cfg ); } + + public function validateAuthenticationBackend() + { + return true; + } } diff --git a/application/forms/Config/Authentication/LdapBackendForm.php b/application/forms/Config/Authentication/LdapBackendForm.php index 087761bd9..58acb995c 100644 --- a/application/forms/Config/Authentication/LdapBackendForm.php +++ b/application/forms/Config/Authentication/LdapBackendForm.php @@ -28,10 +28,13 @@ 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\Web\Form\Decorator\HelpText; use \Icinga\Application\DbAdapterFactory; use \Icinga\Web\Form; @@ -133,6 +136,7 @@ class LdapBackendForm extends BaseBackendForm $this->setSubmitLabel('{{SAVE_ICON}} Save Backend'); } + /** * Return the ldap authentication backend configuration for this form * @@ -160,4 +164,28 @@ class LdapBackendForm extends BaseBackendForm $section => $cfg ); } + + + + public function validateAuthenticationBackend() + { + try { + $cfg = $this->getConfig(); + $testConn = new LdapUserBackend( + new Zend_Config($cfg[$this->getValue('backend_' . $this->filterName($this->getBackendName()) . '_name')]) + ); + + 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..9a809536f --- /dev/null +++ b/application/forms/Config/Authentication/ReorderForm.php @@ -0,0 +1,266 @@ + + * @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\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; +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->addElement( + 'hidden', + $this->targetName, + array( + 'value' => $this->removeTarget, + 'required' => true + ) + ); + $this->setSubmitLabel('{{REMOVE_ICON}} Confirm Removal'); + } +} 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/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php index 25e85eada..339bb7293 100644 --- a/library/Icinga/Application/Config.php +++ b/library/Icinga/Application/Config.php @@ -37,24 +37,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 +66,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 +87,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)); } diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index a3c076df8..7bf792667 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -356,8 +356,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/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php index 52fc51704..a87da116b 100644 --- a/library/Icinga/Authentication/Backend/LdapUserBackend.php +++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php @@ -120,9 +120,9 @@ class LdapUserBackend implements UserBackend public function authenticate(Credentials $credentials) { if (!$this->connection->testCredentials( - $this->connection->fetchDN($this->selectUsername($credentials->getUsername())), - $credentials->getPassword() - ) + $this->connection->fetchDN($this->selectUsername($credentials->getUsername())), + $credentials->getPassword() + ) ) { return false; } @@ -130,4 +130,16 @@ class LdapUserBackend implements UserBackend 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/Web/Form.php b/library/Icinga/Web/Form.php index b530f3a4a..23e85b39e 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,8 @@ 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 +323,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 +337,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 +348,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 *