Split up authentication form, logic connectivity check

refs #4606
refs #4622
refs #4602
refs #4546
This commit is contained in:
Jannis Moßhammer 2013-08-26 16:56:23 +02:00
parent c5dca9062b
commit 17e6402aa9
15 changed files with 690 additions and 451 deletions

View File

@ -33,10 +33,11 @@ use \Icinga\Web\Hook\Configuration\ConfigurationTabBuilder;
use \Icinga\Application\Icinga; use \Icinga\Application\Icinga;
use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Config as IcingaConfig;
use \Icinga\Form\Config\GeneralForm; 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\LdapBackendForm;
use \Icinga\Form\Config\Authentication\DbBackendForm; use \Icinga\Form\Config\Authentication\DbBackendForm;
use \Icinga\Form\Config\LoggingForm; use \Icinga\Form\Config\LoggingForm;
use \Icinga\Form\Config\ConfirmRemovalForm;
use \Icinga\Config\PreservingIniWriter; use \Icinga\Config\PreservingIniWriter;
/** /**
@ -44,6 +45,7 @@ use \Icinga\Config\PreservingIniWriter;
*/ */
class ConfigController extends BaseConfigController class ConfigController extends BaseConfigController
{ {
/** /**
* Create tabs for this configuration controller * Create tabs for this configuration controller
* *
@ -97,7 +99,7 @@ class ConfigController extends BaseConfigController
$form->setRequest($this->_request); $form->setRequest($this->_request);
if ($form->isSubmittedAndValid()) { if ($form->isSubmittedAndValid()) {
if (!$this->writeConfigFile($form->getConfig(), 'config')) { if (!$this->writeConfigFile($form->getConfig(), 'config')) {
return false; return;
} }
$this->redirectNow('/config'); $this->redirectNow('/config');
} }
@ -158,24 +160,69 @@ class ConfigController extends BaseConfigController
/** /**
* Action for creating a new authentication backend * Action for creating a new authentication backend
*/ */
public function authenticationAction() public function authenticationAction($showOnly = false)
{ {
$form = new AuthenticationForm(); $config = IcingaConfig::app('authentication', true);
$config = IcingaConfig::app('authentication'); $order = array_keys($config->toArray());
$form->setConfiguration($config); $this->view->backends = array();
$form->setRequest($this->_request);
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()) { if ($form->isSubmittedAndValid()) {
$modifiedConfig = $form->getConfig(); unset($configArray[$authBackend]);
if (empty($modifiedConfig)) { if ($this->writeAuthenticationFile($configArray)) {
$form->addError('You need at least one authentication backend.'); $this->view->successMessage = 'Authentication Backend "' . $authBackend . '" Removed';
} else if (!$this->writeAuthenticationFile($modifiedConfig)) { $this->authenticationAction(true);
return;
} else {
$this->redirectNow('/config/authentication');
} }
return;
} }
$this->view->form = $form; $this->view->form = $form;
$this->view->name = $authBackend;
$this->render('authentication/remove');
} }
/** /**
@ -188,18 +235,78 @@ class ConfigController extends BaseConfigController
} else { } else {
$form = new DbBackendForm(); $form = new DbBackendForm();
} }
if ($this->getParam('auth_backend')) {
$form->setBackendName($this->getParam('auth_backend'));
}
$form->setRequest($this->getRequest()); $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()) { if ($form->isSubmittedAndValid()) {
$backendCfg = IcingaConfig::app('authentication')->toArray(); $backendCfg = IcingaConfig::app('authentication')->toArray();
foreach ($form->getConfig() as $backendName => $settings) { foreach ($form->getConfig() as $backendName => $settings) {
$backendCfg[$backendName] = $settings; $backendCfg[$backendName] = $settings;
// Remove the old section if the backend is renamed
if ($backendName != $authBackend) {
unset($backendCfg[$authBackend]);
}
} }
if (!$this->writeAuthenticationFile($backendCfg)) { if ($this->writeAuthenticationFile($backendCfg)) {
return; // 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->view->form = $form;
$this->render('authentication/modify'); $this->render('authentication/modify');
} }

View File

@ -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 * Return an array containing all sections defined by this form as the key and all settings
* as an key-value sub-array * as an key-value sub-array
@ -136,4 +178,14 @@ abstract class BaseBackendForm extends Form
* @return array * @return array
*/ */
abstract public function getConfig(); 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();
} }

View File

@ -29,6 +29,7 @@
namespace Icinga\Form\Config\Authentication; namespace Icinga\Form\Config\Authentication;
use Icinga\Authentication\Backend\DbUserBackend;
use \Zend_Config; use \Zend_Config;
use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Config as IcingaConfig;
use \Icinga\Application\Icinga; use \Icinga\Application\Icinga;
@ -118,4 +119,9 @@ class DbBackendForm extends BaseBackendForm
$section => $cfg $section => $cfg
); );
} }
public function validateAuthenticationBackend()
{
return true;
}
} }

View File

@ -28,10 +28,13 @@
namespace Icinga\Form\Config\Authentication; namespace Icinga\Form\Config\Authentication;
use \Icinga\Authentication\Backend\LdapUserBackend;
use \Exception;
use \Zend_Config; use \Zend_Config;
use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Config as IcingaConfig;
use \Icinga\Application\Icinga; use \Icinga\Application\Icinga;
use \Icinga\Application\Logger; use \Icinga\Application\Logger;
use \Icinga\Web\Form\Decorator\HelpText;
use \Icinga\Application\DbAdapterFactory; use \Icinga\Application\DbAdapterFactory;
use \Icinga\Web\Form; use \Icinga\Web\Form;
@ -133,6 +136,7 @@ class LdapBackendForm extends BaseBackendForm
$this->setSubmitLabel('{{SAVE_ICON}} Save Backend'); $this->setSubmitLabel('{{SAVE_ICON}} Save Backend');
} }
/** /**
* Return the ldap authentication backend configuration for this form * Return the ldap authentication backend configuration for this form
* *
@ -160,4 +164,28 @@ class LdapBackendForm extends BaseBackendForm
$section => $cfg $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;
}
} }

View File

@ -0,0 +1,266 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\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:
*
* <pre>
* $array = array('first', 'second', 'third');
* moveElementUp('third', $array); // returns ['first', 'third', 'second']
* </pre>
*
* @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<count($array)-1; $i++) {
if ($array[$i+1] !== $key) {
continue;
}
$swap = $array[$i];
$array[$i] = $array[$i+1];
$array[$i+1] = $swap;
return $array;
}
return $array;
}
/**
* Static helper for moving an element in an array one slot up, if possible
*
* Example:
*
* <pre>
* $array = array('first', 'second', 'third');
* moveElementDown('first', $array); // returns ['second', 'first', 'third']
* </pre>
*
* @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<count($array)-1; $i++) {
if ($array[$i] !== $key) {
continue;
}
$swap = $array[$i+1];
$array[$i+1] = $array[$i];
$array[$i] = $swap;
return $array;
}
return $array;
}
}

View File

@ -1,385 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
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:
*
* <pre>
* $array = array('first', 'second', 'third');
* moveElementUp('third', $array); // returns ['first', 'third', 'second']
* </pre>
*
* @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<count($array)-1; $i++) {
if ($array[$i+1] !== $key) {
continue;
}
$swap = $array[$i];
$array[$i] = $array[$i+1];
$array[$i+1] = $swap;
return $array;
}
return $array;
}
/**
* Static helper for moving an element in an array one slot up, if possible
*
* Example:
*
* <pre>
* $array = array('first', 'second', 'third');
* moveElementDown('first', $array); // returns ['second', 'first', 'third']
* </pre>
*
* @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<count($array)-1; $i++) {
if ($array[$i] !== $key) {
continue;
}
$swap = $array[$i+1];
$array[$i+1] = $array[$i];
$array[$i] = $swap;
return $array;
}
return $array;
}
}

View File

@ -0,0 +1,82 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
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');
}
}

View File

@ -1,35 +1,56 @@
<?php <?php
use Icinga\Web\Url; use Icinga\Web\Url;
$createLdapBackend = Url::fromPath( $createLdapBackend = $this->href('/config/createAuthenticationBackend', array('type' => 'ldap'));
'/config/createAuthenticationBackend', $createDbBackend = $this->href('/config/createAuthenticationBackend', array('type' => 'db'));
array('type' => 'ldap')
)->getAbsoluteUrl();
$createDbBackend = Url::fromPath(
'/config/createAuthenticationBackend',
array('type' => 'db')
)->getAbsoluteUrl();
?> ?>
<?= $this->tabs->render($this); ?> <?= $this->tabs->render($this); ?>
<?php <?php if ($this->errorMessage): ?>
$errors = $this->form->getErrorMessages(); <div class="alert alert-danger">
if (!empty($errors)): <i>{{ERROR_ICON}}</i>
?> <strong><?= $this->escape($this->errorMessage); ?></strong>
<div class="alert alert-danger"> </div>
<h4>{{ERROR_ICON}} There are errors in your configuration:</h4>
<br/>
<ul>
<?php foreach($errors as $error): ?>
<li><?= $error ?></li>
<?php endforeach;?>
</ul>
</div>
<?php endif; ?> <?php endif; ?>
<div>
<a href="<?= $createLdapBackend ?>">{{CREATE_ICON}} Create A New LDAP Authentication Backend</a><br/> <?php if ($this->successMessage): ?>
<a href="<?= $createDbBackend ?>">{{CREATE_ICON}} Create A New DB Authentication Backend</a> <div class="alert alert-success">
<i>{{OK_ICON}}</i>
<strong><?= $this->escape($this->successMessage); ?></strong>
</div> </div>
<?= $this->form ?> <?php endif; ?>
<div class="panel panel-default">
<div class="panel-heading panel-title">
Create Authentication Provider
</div>
<div class="panel-body">
<a href="<?= $createLdapBackend ?>">{{CREATE_ICON}} Create A New LDAP Authentication Backend</a><br/>
<a href="<?= $createDbBackend ?>">{{CREATE_ICON}} Create A New DB Authentication Backend</a>
</div>
</div>
<?php foreach ($this->backends as $backend): ?>
<div class="panel panel-default">
<div class="panel-heading panel-title">
Backend <?= $this->escape($backend->name); ?>
<br/>
<?= $backend->reorderForm; ?>
</div>
<div class="panel-body">
<a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backend->name));?>">
{{EDIT_ICON}} Edit This Authentication Provider
</a>
<br/>
<?php if (count($this->backends) > 1): ?>
<a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backend->name));?>">
{{REMOVE_ICON}} Remove This Authentication Provider
</a>
<br/>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>

View File

@ -0,0 +1,15 @@
<?= $this->tabs->render($this); ?>
<h4>{{CREATE_ICON}} Create New Authentication Backend</h4>
<?php if ($this->errorMessage): ?>
<div class="alert alert-danger">
<?= $this->escape($this->errorMessage); ?>
</div>
<?php endif; ?>
<p>
Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.
</p>
<?= $this->form ?>

View File

@ -1,4 +1,14 @@
<?= $this->tabs->render($this); ?> <?= $this->tabs->render($this); ?>
<h4>{{CREATE_ICON}} Create New Backend</h4> <h4>{{CREATE_ICON}} Edit Backend "<?= $this->escape($this->name); ?>"</h4>
<?php if ($this->errorMessage || $this->form->getErrorMessages()): ?>
<div class="alert alert-danger">
<?= $this->escape($this->errorMessage); ?>
<?php foreach ($this->form->getErrorMessages() as $error): ?>
<?= $this->escape($error); ?><br/>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?= $this->form ?> <?= $this->form ?>

View File

@ -0,0 +1,4 @@
<?= $this->tabs->render($this); ?>
<h4>{{REMOVE_ICON}} Remove Backend "<?= $this->escape($this->name); ?>"</h4>
<?= $this->form ?>

View File

@ -37,24 +37,28 @@ class Config extends Zend_Config_Ini
{ {
/** /**
* Configuration directory where ALL (application and module) configuration is located * Configuration directory where ALL (application and module) configuration is located
*
* @var string * @var string
*/ */
public static $configDir; public static $configDir;
/** /**
* The INI file this configuration has been loaded from * The INI file this configuration has been loaded from
*
* @var string * @var string
*/ */
private $configFile; private $configFile;
/** /**
* Application config instances per file * Application config instances per file
*
* @var array * @var array
*/ */
protected static $app = array(); protected static $app = array();
/** /**
* Module config instances per file * Module config instances per file
*
* @var array * @var array
*/ */
protected static $modules = array(); protected static $modules = array();
@ -62,10 +66,10 @@ class Config extends Zend_Config_Ini
/** /**
* Load configuration from the config file $filename * Load configuration from the config file $filename
* *
* @param string $filename The filename to parse
* @see Zend_Config_Ini::__construct * @see Zend_Config_Ini::__construct
* * @throws Exception When the file can't be read
* @param string $filename
* @throws Exception
*/ */
public function __construct($filename) public function __construct($filename)
{ {
@ -83,12 +87,15 @@ class Config extends Zend_Config_Ini
/** /**
* Retrieve a application config instance * Retrieve a application config instance
* *
* @param string $configname * @param string $configname The configuration name (without ini suffix) to read and return
* @return mixed * @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'; $filename = self::$configDir . '/' . $configname . '.ini';
self::$app[$configname] = new Config(realpath($filename)); self::$app[$configname] = new Config(realpath($filename));
} }

View File

@ -356,8 +356,6 @@ class Web extends ApplicationBootstrap
$view->setView(new View()); $view->setView(new View());
$view->view->addHelperPath($this->getApplicationDir('/views/helpers')); $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->setEncoding('UTF-8');
$view->view->headTitle()->prepend( $view->view->headTitle()->prepend(

View File

@ -120,9 +120,9 @@ class LdapUserBackend implements UserBackend
public function authenticate(Credentials $credentials) public function authenticate(Credentials $credentials)
{ {
if (!$this->connection->testCredentials( if (!$this->connection->testCredentials(
$this->connection->fetchDN($this->selectUsername($credentials->getUsername())), $this->connection->fetchDN($this->selectUsername($credentials->getUsername())),
$credentials->getPassword() $credentials->getPassword()
) )
) { ) {
return false; return false;
} }
@ -130,4 +130,16 @@ class LdapUserBackend implements UserBackend
return $user; return $user;
} }
public function getUserCount() {
return $this->connection->count(
$this->connection->select()->from(
$this->config->user_class,
array(
$this->config->user_name_attribute
)
)
);
}
} }

View File

@ -38,7 +38,7 @@ use \Icinga\Web\Form\Element\Note;
/** /**
* Base class for forms providing CSRF protection, confirmation logic and auto submission * 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 * The form's request object
@ -161,7 +161,8 @@ abstract class Form extends Zend_Form
/** /**
* Add elements to this form (used by extending classes) * Add elements to this form (used by extending classes)
*/ */
abstract protected function create(); protected function create()
{}
/** /**
* Method called before validation * 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 * 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. * 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() public function isSubmittedAndValid()
{ {
@ -334,12 +337,7 @@ abstract class Form extends Zend_Form
$checkData = $this->getRequest()->getParams(); $checkData = $this->getRequest()->getParams();
$this->assertValidCsrfToken($checkData); $this->assertValidCsrfToken($checkData);
$submitted = true; if ($this->isSubmitted()) {
if ($this->submitLabel) {
$submitted = isset($checkData['btn_submit']);
}
if ($submitted) {
// perform full validation if submitted // perform full validation if submitted
$this->preValidation($checkData); $this->preValidation($checkData);
return $this->isValid($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 * Disable CSRF counter measure and remove its field if already added
* *