icingaweb2/application/forms/Config/AuthenticationForm.php

397 lines
13 KiB
PHP
Raw Normal View History

2013-08-15 14:27:53 +02:00
<?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\Application\Config as IcingaConfig;
use \Icinga\Application\Icinga;
use \Icinga\Application\Logger;
use \Icinga\Application\DbAdapterFactory;
use \Icinga\Web\Form;
use \Icinga\Web\Form\Element\Note;
use \Icinga\Web\Form\Decorator\ConditionalHidden;
use \Zend_Config;
/**
* Form for modifying the authentication provider and order.
*
* This is a composite form from one or more forms under the Authentication folder
*/
2013-08-15 14:27:53 +02:00
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();
2013-08-15 14:27:53 +02:00
/**
* 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)
{
2013-08-15 14:27:53 +02:00
$this->addElement(
'checkbox',
2013-08-15 14:27:53 +02:00
'backend_' . $name . '_remove',
array(
'name' => 'backend_' . $name . '_remove',
'label' => 'Remove this authentication provider',
'value' => $name,
'checked' => $this->isMarkedForDeletion($name)
2013-08-15 14:27:53 +02:00
)
);
$this->enableAutoSubmit(array('backend_' . $name . '_remove'));
return 'backend_' . $name . '_remove';
2013-08-15 14:27:53 +02:00
}
/**
* 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)
2013-08-15 14:27:53 +02:00
{
$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;
}
2013-08-15 14:27:53 +02:00
$form = new $formClass();
$form->setBackendName($name);
$form->setBackend($backend);
2013-08-15 14:27:53 +02:00
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;
}
2013-08-15 14:27:53 +02:00
/**
* 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())
2013-08-15 14:27:53 +02:00
{
$formEls = array();
$priorities = array(
'up' => join(',', self::moveElementUp($name, $order)),
'down' => join(',', self::moveElementDown($name, $order))
);
if ($priorities['up'] != join(',', $order)) {
2013-08-15 14:27:53 +02:00
$this->addElement(
'button',
'priority' . $name . '_up',
2013-08-15 14:27:53 +02:00
array(
'name' => 'priority',
2013-08-15 14:27:53 +02:00
'label' => 'Move up in authentication order',
'value' => $priorities['up'],
'type' => 'submit'
2013-08-15 14:27:53 +02:00
)
);
$formEls[] = 'priority' . $name . '_up';
2013-08-15 14:27:53 +02:00
}
if ($priorities['down'] != join(',', $order)) {
2013-08-15 14:27:53 +02:00
$this->addElement(
'button',
'priority' . $name . '_down',
2013-08-15 14:27:53 +02:00
array(
'name' => 'priority',
2013-08-15 14:27:53 +02:00
'label' => 'Move down in authentication order',
'value' => $priorities['down'],
'type' => 'submit'
2013-08-15 14:27:53 +02:00
)
);
$formEls[] = 'priority' . $name . '_down';
2013-08-15 14:27:53 +02:00
}
return $formEls;
2013-08-15 14:27:53 +02:00
}
/**
* 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 void|\Zend_Form
* @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 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()
2013-08-15 14:27:53 +02:00
{
$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()
{
2013-08-15 14:27:53 +02:00
$this->addElement(
'hidden',
'current_priority',
2013-08-15 14:27:53 +02:00
array(
'name' => 'current_priority',
'value' => join(',', $this->getAuthenticationOrder())
2013-08-15 14:27:53 +02:00
)
);
}
/**
* Create the authentication provider configuration form
*
* @see IcingaForm::create()
*/
public function create()
{
$order = $this->getAuthenticationOrder();
foreach ($order as $name) {
$this->addElement(
new Note(
array(
'escape' => false,
'name' => 'title_backend_' . $name,
'value' => '<h4>Backend ' . $name . '</h4>'
)
)
);
$this->addRemoveHint($this->filterName($name));
$backend = $this->config->get($name, null);
if ($backend === null) {
2013-08-15 14:27:53 +02:00
continue;
}
if (!$this->isMarkedForDeletion($this->filterName($name))) {
$this->addProviderForm($name, $backend);
$this->addPriorityButtons($name, $order);
}
2013-08-15 14:27:53 +02:00
}
$this->addPersistentState();
$this->enableConditionalDecorator();
2013-08-20 17:30:28 +02:00
$this->setSubmitLabel('Save Changes');
2013-08-15 14:27:53 +02:00
}
/**
* 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;
}
}