* @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\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 */ 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 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() { $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->addElement( new Note( array( 'escape' => false, 'name' => 'title_backend_' . $name, 'value' => '

Backend ' . $name . '

' ) ) ); $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 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