Adjust authentication backend forms to suit.. some form implementation
refs #5525
This commit is contained in:
parent
364c7c0858
commit
ae35650c7e
|
@ -6,19 +6,16 @@ use Icinga\Web\Controller\BaseConfigController;
|
|||
use Icinga\Web\Widget\AlertMessageBox;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Widget;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Config as IcingaConfig;
|
||||
use Icinga\Form\Config\GeneralForm;
|
||||
use Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||
use Icinga\Form\Config\Authentication\DbBackendForm;
|
||||
use Icinga\Form\Config\Authentication\AutologinBackendForm;
|
||||
use Icinga\Form\Config\AuthenticationBackendReorderForm;
|
||||
use Icinga\Form\Config\AuthenticationBackendConfigForm;
|
||||
use Icinga\Form\Config\ResourceForm;
|
||||
use Icinga\Form\Config\LoggingForm;
|
||||
use Icinga\Form\Config\ConfirmRemovalForm;
|
||||
use Icinga\Config\PreservingIniWriter;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -142,47 +139,17 @@ class ConfigController extends BaseConfigController
|
|||
}
|
||||
|
||||
/**
|
||||
* Action for reordering authentication backends
|
||||
* Action for listing and reordering authentication backends
|
||||
*/
|
||||
public function authenticationAction()
|
||||
{
|
||||
$this->view->messageBox = new AlertMessageBox(true);
|
||||
$form = new AuthenticationBackendReorderForm();
|
||||
$form->setConfig(IcingaConfig::app('authentication'));
|
||||
$form->handleRequest();
|
||||
|
||||
$this->view->form = $form;
|
||||
$this->view->tabs->activate('authentication');
|
||||
|
||||
$config = IcingaConfig::app('authentication');
|
||||
$backendOrder = array_keys($config->toArray());
|
||||
$form = new Form();
|
||||
$form->setName('form_reorder_authbackend');
|
||||
$request = $this->getRequest();
|
||||
if ($request->isPost()) {
|
||||
$requestData = $request->getPost();
|
||||
if ($form->isValid($requestData)) { // Validate the CSRF token
|
||||
$reordered = false;
|
||||
foreach ($backendOrder as $backendName) {
|
||||
if (isset($requestData[$backendName])) {
|
||||
array_splice($backendOrder, array_search($backendName, $backendOrder), 1);
|
||||
array_splice($backendOrder, $requestData[$backendName], 0, $backendName);
|
||||
$reordered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($reordered) {
|
||||
$reorderedConfig = array();
|
||||
foreach ($backendOrder as $backendName) {
|
||||
$reorderedConfig[$backendName] = $config->{$backendName};
|
||||
}
|
||||
|
||||
if ($this->writeAuthenticationFile($reorderedConfig)) {
|
||||
Notification::success($this->translate('Authentication order updated!'));
|
||||
$this->redirectNow('config/authentication');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->form = $form->create(); // Necessary in case its a GET request
|
||||
$this->view->backendNames = $backendOrder;
|
||||
$this->render('authentication/reorder');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,172 +157,66 @@ class ConfigController extends BaseConfigController
|
|||
*/
|
||||
public function createauthenticationbackendAction()
|
||||
{
|
||||
$this->view->messageBox = new AlertMessageBox(true);
|
||||
$this->view->tabs->activate('authentication');
|
||||
$form = new AuthenticationBackendConfigForm();
|
||||
$form->setConfig(IcingaConfig::app('authentication'));
|
||||
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
|
||||
$form->setRedirectUrl('config/authentication');
|
||||
$form->handleRequest();
|
||||
|
||||
$backendType = $this->getRequest()->getParam('type');
|
||||
$authenticationConfig = IcingaConfig::app('authentication')->toArray();
|
||||
try {
|
||||
switch ($backendType) {
|
||||
case 'ldap':
|
||||
$form = new LdapBackendForm();
|
||||
break;
|
||||
case 'db':
|
||||
$form = new DbBackendForm();
|
||||
break;
|
||||
case 'autologin':
|
||||
foreach ($authenticationConfig as $ac) {
|
||||
if (array_key_exists('backend', $ac) && $ac['backend'] === 'autologin') {
|
||||
throw new ConfigurationError(
|
||||
$this->translate('An autologin backend already exists')
|
||||
);
|
||||
}
|
||||
}
|
||||
$form = new AutologinBackendForm();
|
||||
break;
|
||||
default:
|
||||
$this->addErrorMessage(sprintf(
|
||||
$this->translate('There is no backend type `%s\''),
|
||||
$backendType
|
||||
));
|
||||
$this->redirectNow('config/configurationerror');
|
||||
}
|
||||
} catch (ConfigurationError $e) {
|
||||
$this->addErrorMessage($e->getMessage());
|
||||
$this->redirectNow('config/configurationerror');
|
||||
}
|
||||
|
||||
$request = $this->getRequest();
|
||||
if ($request->isPost() && $form->isValid($request->getPost())) {
|
||||
list($backendName, $backendConfig) = $form->getBackendConfig();
|
||||
if (isset($authenticationConfig[$backendName])) {
|
||||
$this->addErrorMessage(
|
||||
$this->translate('Backend name already exists')
|
||||
);
|
||||
} else {
|
||||
$authenticationConfig[$backendName] = $backendConfig;
|
||||
if ($this->writeConfigFile($authenticationConfig, 'authentication')) {
|
||||
$this->addSuccessMessage(
|
||||
$this->translate('Backend Modification Written.')
|
||||
);
|
||||
$this->redirectNow('config/authentication');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->messageBox->addForm($form);
|
||||
$this->view->form = $form;
|
||||
$this->view->tabs->activate('authentication');
|
||||
$this->render('authentication/create');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Form for editing backends
|
||||
*
|
||||
* Mostly the same like the createAuthenticationBackendAction, but with additional checks for backend existence
|
||||
* and form population
|
||||
* Action for editing authentication backends
|
||||
*/
|
||||
public function editauthenticationbackendAction()
|
||||
{
|
||||
$this->view->messageBox = new AlertMessageBox(true);
|
||||
$form = new AuthenticationBackendConfigForm();
|
||||
$form->setConfig(IcingaConfig::app('authentication'));
|
||||
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
|
||||
$form->setRedirectUrl('config/authentication');
|
||||
$form->handleRequest();
|
||||
|
||||
$configArray = IcingaConfig::app('authentication', true)->toArray();
|
||||
$authBackend = $this->getParam('auth_backend');
|
||||
if (false === isset($configArray[$authBackend])) {
|
||||
$this->addErrorMessage(
|
||||
$this->translate('Can\'t edit: Unknown Authentication Backend Provided')
|
||||
);
|
||||
$this->redirectNow('config/configurationerror');
|
||||
}
|
||||
|
||||
if (false === array_key_exists('backend', $configArray[$authBackend])) {
|
||||
$this->addErrorMessage(sprintf(
|
||||
$this->translate('Backend "%s" has no `backend\' setting'),
|
||||
$authBackend
|
||||
));
|
||||
$this->redirectNow('config/configurationerror');
|
||||
}
|
||||
$type = $configArray[$authBackend]['backend'];
|
||||
switch ($type) {
|
||||
case 'ldap':
|
||||
$form = new LdapBackendForm();
|
||||
break;
|
||||
case 'db':
|
||||
$form = new DbBackendForm();
|
||||
break;
|
||||
case 'autologin':
|
||||
$form = new AutologinBackendForm();
|
||||
break;
|
||||
default:
|
||||
$this->addErrorMessage(sprintf(
|
||||
$this->translate('Can\'t edit: backend type "%s" of given resource not supported.'),
|
||||
$type
|
||||
));
|
||||
$this->redirectNow('config/configurationerror');
|
||||
}
|
||||
|
||||
$request = $this->getRequest();
|
||||
if ($request->isPost()) {
|
||||
if ($form->isValid($request->getPost())) {
|
||||
list($backendName, $backendConfig) = $form->getBackendConfig();
|
||||
$configArray[$backendName] = $backendConfig;
|
||||
if ($backendName != $authBackend) {
|
||||
unset($configArray[$authBackend]);
|
||||
}
|
||||
if ($this->writeAuthenticationFile($configArray)) {
|
||||
// redirect to overview with success message
|
||||
Notification::success(sprintf(
|
||||
$this->translate('Backend "%s" saved'),
|
||||
$backendName
|
||||
));
|
||||
$this->redirectNow('config/authentication');
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$form->setBackendConfig($authBackend, $configArray[$authBackend]);
|
||||
}
|
||||
|
||||
$this->view->messageBox->addForm($form);
|
||||
$this->view->name = $authBackend;
|
||||
$this->view->form = $form;
|
||||
$this->view->tabs->activate('authentication');
|
||||
$this->render('authentication/modify');
|
||||
}
|
||||
|
||||
/**
|
||||
* Action for removing a backend from the authentication list.
|
||||
*
|
||||
* Redirects to the overview after removal is finished
|
||||
* Action for removing a backend from the authentication list
|
||||
*/
|
||||
public function removeauthenticationbackendAction()
|
||||
{
|
||||
$this->view->messageBox = new AlertMessageBox(true);
|
||||
$form = new ConfirmRemovalForm(array(
|
||||
'onSuccess' => function ($request) {
|
||||
$configForm = new AuthenticationBackendConfigForm();
|
||||
$configForm->setConfig(IcingaConfig::app('authentication'));
|
||||
$authBackend = $request->getQuery('auth_backend');
|
||||
|
||||
$configArray = IcingaConfig::app('authentication', true)->toArray();
|
||||
$authBackend = $this->getParam('auth_backend');
|
||||
if (false === array_key_exists($authBackend, $configArray)) {
|
||||
$this->addErrorMessage(
|
||||
$this->translate('Can\'t perform removal: Unknown authentication backend provided')
|
||||
);
|
||||
$this->redirectNow('config/configurationerror');
|
||||
}
|
||||
try {
|
||||
$configForm->remove($authBackend);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
Notification::error($e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
$form = new ConfirmRemovalForm();
|
||||
$request = $this->getRequest();
|
||||
|
||||
if ($request->isPost() && $form->isValid($request->getPost())) {
|
||||
unset($configArray[$authBackend]);
|
||||
if ($this->writeAuthenticationFile($configArray)) {
|
||||
Notification::success(sprintf(
|
||||
$this->translate('Authentication Backend "%s" Removed'),
|
||||
$authBackend
|
||||
));
|
||||
$this->redirectNow('config/authentication');
|
||||
if ($configForm->save()) {
|
||||
Notification::success(sprintf(
|
||||
t('Authentication backend "%s" has been successfully removed'),
|
||||
$authBackend
|
||||
));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
$form->setRedirectUrl('config/authentication');
|
||||
$form->handleRequest();
|
||||
|
||||
$this->view->form = $form;
|
||||
$this->view->name = $authBackend;
|
||||
$this->view->tabs->activate('authentication');
|
||||
$this->render('authentication/remove');
|
||||
}
|
||||
|
||||
|
|
|
@ -5,19 +5,19 @@
|
|||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use Zend_Validate_Callback;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Form class for adding/modifying autologin authentication backends
|
||||
*/
|
||||
class AutologinBackendForm extends BaseBackendForm
|
||||
class AutologinBackendForm extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this form
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_config_authentication_autologin');
|
||||
$this->setSubmitLabel(t('Save Changes'));
|
||||
$this->setName('form_config_authbackend_autologin');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,6 @@ class AutologinBackendForm extends BaseBackendForm
|
|||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'allowEmpty' => false,
|
||||
'label' => t('Backend Name'),
|
||||
'helptext' => t('The name of this authentication backend'),
|
||||
'validators' => array(
|
||||
|
@ -53,7 +52,6 @@ class AutologinBackendForm extends BaseBackendForm
|
|||
'strip_username_regexp',
|
||||
array(
|
||||
'required' => true,
|
||||
'allowEmpty' => false,
|
||||
'label' => t('Backend Domain Pattern'),
|
||||
'helptext' => t('The domain pattern of this authentication backend'),
|
||||
'value' => '/\@[^$]+$/',
|
||||
|
@ -76,13 +74,15 @@ class AutologinBackendForm extends BaseBackendForm
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate the configuration state of this backend
|
||||
* Validate the configuration by creating a backend and requesting the user count
|
||||
*
|
||||
* Returns just true as autologins are being handled externally by the webserver.
|
||||
* Returns always true as autologin backends are just "passive" backends. (The webserver authenticates users.)
|
||||
*
|
||||
* @return true
|
||||
* @param Form $form The form to fetch the configuration values from
|
||||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidAuthenticationBackend()
|
||||
public function isValidAuthenticationBackend(Form $form)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Base form for authentication backend forms
|
||||
*/
|
||||
abstract class BaseBackendForm extends Form
|
||||
{
|
||||
/**
|
||||
* Return whether the given values are complete/valid and check whether it is possible to connect to the backend
|
||||
*
|
||||
* If connection validation fails, a checkbox is prepended to the form to allow users to skip it.
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool Whether the validation succeeded or not
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
(false === isset($data['force_creation']) || false === $data['force_creation'])
|
||||
&& false === $this->isValidAuthenticationBackend()
|
||||
) {
|
||||
$this->addForceCreationCheckbox();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 'force_creation' checkbox is set, this method won't be called.
|
||||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
abstract public function isValidAuthenticationBackend();
|
||||
|
||||
/**
|
||||
* Return the backend's configuration values and its name
|
||||
*
|
||||
* The first value is the name and the second one the values as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBackendConfig()
|
||||
{
|
||||
$values = $this->getValues();
|
||||
$name = $values['name'];
|
||||
unset($values['name']);
|
||||
return array($name, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the form with the given configuration values
|
||||
*
|
||||
* @param string $name The name of the backend
|
||||
* @param array $config The configuration values
|
||||
*/
|
||||
public function setBackendConfig($name, array $config)
|
||||
{
|
||||
$config['name'] = $name;
|
||||
$this->populate($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to be displayed at the beginning of the form
|
||||
* which allows the user to skip the connection validation
|
||||
*/
|
||||
protected function addForceCreationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'force_creation',
|
||||
array(
|
||||
'order' => 0,
|
||||
'ignore' => true,
|
||||
'label' => t('Force Changes'),
|
||||
'helptext' => t('Check this box to enforce changes without connectivity validation')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,17 +5,18 @@
|
|||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Authentication\Backend\DbUserBackend;
|
||||
|
||||
/**
|
||||
* Form class for adding/modifying database authentication backends
|
||||
*/
|
||||
class DbBackendForm extends BaseBackendForm
|
||||
class DbBackendForm extends Form
|
||||
{
|
||||
/**
|
||||
* The available database resources prepared to be used as select input data
|
||||
* The database resource names the user can choose from
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -23,28 +24,23 @@ class DbBackendForm extends BaseBackendForm
|
|||
|
||||
/**
|
||||
* Initialize this form
|
||||
*
|
||||
* Populates $this->resources.
|
||||
*
|
||||
* @throws ConfigurationError In case no database resources can be found
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_config_authentication_db');
|
||||
$this->setSubmitLabel(t('Save Changes'));
|
||||
$this->setName('form_config_authbackend_db');
|
||||
}
|
||||
|
||||
$dbResources = array_keys(
|
||||
ResourceFactory::getResourceConfigs('db')->toArray()
|
||||
);
|
||||
|
||||
if (empty($dbResources)) {
|
||||
throw new ConfigurationError(
|
||||
t('There are no database resources')
|
||||
);
|
||||
}
|
||||
|
||||
// array_combine() is necessary in order to use the array as select input data
|
||||
$this->resources = array_combine($dbResources, $dbResources);
|
||||
/**
|
||||
* Set the resource names the user can choose from
|
||||
*
|
||||
* @param array $resources The resources to choose from
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setResources(array $resources)
|
||||
{
|
||||
$this->resources = $resources;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +65,9 @@ class DbBackendForm extends BaseBackendForm
|
|||
'required' => true,
|
||||
'label' => t('Database Connection'),
|
||||
'helptext' => t('The database connection to use for authenticating with this provider'),
|
||||
'multiOptions' => $this->resources
|
||||
'multiOptions' => false === empty($this->resources)
|
||||
? array_combine($this->resources, $this->resources)
|
||||
: array()
|
||||
)
|
||||
),
|
||||
$this->createElement(
|
||||
|
@ -84,25 +82,39 @@ class DbBackendForm extends BaseBackendForm
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate the current configuration by creating a backend and requesting the user count
|
||||
* Validate that the selected resource is a valid database authentication backend
|
||||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*
|
||||
* @see BaseBackendForm::isValidAuthenticationBackend()
|
||||
* @see Form::onSuccess()
|
||||
*/
|
||||
public function isValidAuthenticationBackend()
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === $this->isValidAuthenticationBackend($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the configuration by creating a backend and requesting the user count
|
||||
*
|
||||
* @param Form $form The form to fetch the configuration values from
|
||||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidAuthenticationBackend(Form $form)
|
||||
{
|
||||
$element = $form->getElement('resource');
|
||||
|
||||
try {
|
||||
$testConnection = ResourceFactory::createResource(ResourceFactory::getResourceConfig(
|
||||
$this->getValue('resource')
|
||||
));
|
||||
$testConnection = ResourceFactory::createResource(
|
||||
ResourceFactory::getResourceConfig($element->getValue())
|
||||
);
|
||||
$dbUserBackend = new DbUserBackend($testConnection);
|
||||
if ($dbUserBackend->count() < 1) {
|
||||
$this->addErrorMessage(t('No users found under the specified database backend'));
|
||||
$element->addError(t('No users found under the specified database backend'));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->addErrorMessage(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
||||
$element->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Authentication\Backend\LdapUserBackend;
|
||||
|
||||
/**
|
||||
* Form for adding or modifying LDAP authentication backends
|
||||
* Form class for adding/modifying LDAP authentication backends
|
||||
*/
|
||||
class LdapBackendForm extends BaseBackendForm
|
||||
class LdapBackendForm extends Form
|
||||
{
|
||||
/**
|
||||
* The available ldap resources prepared to be used as select input data
|
||||
* The ldap resource names the user can choose from
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -23,28 +23,23 @@ class LdapBackendForm extends BaseBackendForm
|
|||
|
||||
/**
|
||||
* Initialize this form
|
||||
*
|
||||
* Populates $this->resources.
|
||||
*
|
||||
* @throws ConfigurationError In case no database resources can be found
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_config_authentication_ldap');
|
||||
$this->setSubmitLabel(t('Save Changes'));
|
||||
$this->setName('form_config_authbackend_ldap');
|
||||
}
|
||||
|
||||
$ldapResources = array_keys(
|
||||
ResourceFactory::getResourceConfigs('ldap')->toArray()
|
||||
);
|
||||
|
||||
if (empty($ldapResources)) {
|
||||
throw new ConfigurationError(
|
||||
t('There are no LDAP resources')
|
||||
);
|
||||
}
|
||||
|
||||
// array_combine() is necessary in order to use the array as select input data
|
||||
$this->resources = array_combine($ldapResources, $ldapResources);
|
||||
/**
|
||||
* Set the resource names the user can choose from
|
||||
*
|
||||
* @param array $resources The resources to choose from
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setResources(array $resources)
|
||||
{
|
||||
$this->resources = $resources;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +64,9 @@ class LdapBackendForm extends BaseBackendForm
|
|||
'required' => true,
|
||||
'label' => t('LDAP Resource'),
|
||||
'helptext' => t('The resource to use for authenticating with this provider'),
|
||||
'multiOptions' => $this->resources
|
||||
'multiOptions' => false === empty($this->resources)
|
||||
? array_combine($this->resources, $this->resources)
|
||||
: array()
|
||||
)
|
||||
),
|
||||
$this->createElement(
|
||||
|
@ -104,33 +101,39 @@ class LdapBackendForm extends BaseBackendForm
|
|||
}
|
||||
|
||||
/**
|
||||
* Validate the current configuration by connecting to a backend and requesting the user count
|
||||
* Validate that the selected resource is a valid ldap authentication backend
|
||||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*
|
||||
* @see BaseBackendForm::isValidAuthenticationBacken()
|
||||
* @see Form::onSuccess()
|
||||
*/
|
||||
public function isValidAuthenticationBackend()
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === ResourceFactory::ldapAvailable()) {
|
||||
// It should be possible to run icingaweb without the php ldap extension. When the user
|
||||
// tries to create an ldap backend without ldap being installed we display an error.
|
||||
$this->addErrorMessage(t('Using ldap is not possible, the php extension "ldap" is not installed.'));
|
||||
if (false === $this->isValidAuthenticationBackend($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the configuration by creating a backend and requesting the user count
|
||||
*
|
||||
* @param Form $form The form to fetch the configuration values from
|
||||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidAuthenticationBackend(Form $form)
|
||||
{
|
||||
$element = $form->getElement('resource');
|
||||
|
||||
try {
|
||||
$backend = ResourceFactory::createResource(
|
||||
ResourceFactory::getResourceConfig($this->getValue('resource'))
|
||||
$ldapUserBackend = new LdapUserBackend(
|
||||
ResourceFactory::createResource(
|
||||
ResourceFactory::getResourceConfig($element->getValue())
|
||||
),
|
||||
$form->getElement('user_class')->getValue(),
|
||||
$form->getElement('user_name_attribute')->getValue()
|
||||
);
|
||||
$testConn = new LdapUserBackend(
|
||||
$backend,
|
||||
$this->getValue('user_class'),
|
||||
$this->getValue('user_name_attribute')
|
||||
);
|
||||
$testConn->assertAuthenticationPossible();
|
||||
} catch (Exception $exc) {
|
||||
$this->addErrorMessage(sprintf(t('Connection validation failed: %s'), $exc->getMessage()));
|
||||
$ldapUserBackend->assertAuthenticationPossible();
|
||||
} catch (Exception $e) {
|
||||
$element->addError(sprintf(t('Connection validation failed: %s'), $e->getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Form\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Form\ConfigForm;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Form\Config\Authentication\DbBackendForm;
|
||||
use Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||
use Icinga\Form\Config\Authentication\AutologinBackendForm;
|
||||
|
||||
class AuthenticationBackendConfigForm extends ConfigForm
|
||||
{
|
||||
/**
|
||||
* The available resources split by type
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $resources;
|
||||
|
||||
/**
|
||||
* Initialize this form
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_config_authbackend');
|
||||
$this->setSubmitLabel(t('Save Changes'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource configuration to use
|
||||
*
|
||||
* @param Config $resources The resource configuration
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws ConfigurationError In case no resources are available for authentication
|
||||
*/
|
||||
public function setResourceConfig(Config $resourceConfig)
|
||||
{
|
||||
$resources = array();
|
||||
foreach ($resourceConfig as $name => $resource) {
|
||||
$resources[strtolower($resource->type)][] = $name;
|
||||
}
|
||||
|
||||
if (empty($resources)) {
|
||||
throw new ConfigurationError(t('Could not find any resources for authentication'));
|
||||
}
|
||||
|
||||
$this->resources = $resources;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a form object for the given backend type
|
||||
*
|
||||
* @param string $type The backend type for which to return a form
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function getBackendForm($type)
|
||||
{
|
||||
if ($type === 'db') {
|
||||
$form = new DbBackendForm();
|
||||
$form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array());
|
||||
} elseif ($type === 'ldap') {
|
||||
$form = new LdapBackendForm();
|
||||
$form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array());
|
||||
} elseif ($type === 'autologin') {
|
||||
$form = new AutologinBackendForm();
|
||||
} else {
|
||||
throw new InvalidArgumentException(sprintf(t('Invalid backend type "%s" provided'), $type));
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a particular authentication backend
|
||||
*
|
||||
* The backend to add is identified by the array-key `name'.
|
||||
*
|
||||
* @param array $values The values to extend the configuration with
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws InvalidArgumentException In case the backend does already exist
|
||||
*/
|
||||
public function add(array $values)
|
||||
{
|
||||
$name = isset($values['name']) ? $values['name'] : '';
|
||||
if (! $name) {
|
||||
throw new InvalidArgumentException(t('Authentication backend name missing'));
|
||||
} elseif ($this->config->get($name) !== null) {
|
||||
throw new InvalidArgumentException(t('Authentication backend already exists'));
|
||||
}
|
||||
|
||||
unset($values['name']);
|
||||
$this->config->{$name} = $values;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a particular authentication backend
|
||||
*
|
||||
* @param string $name The name of the backend to edit
|
||||
* @param array $values The values to edit the configuration with
|
||||
*
|
||||
* @return array The edited backend configuration
|
||||
*
|
||||
* @throws InvalidArgumentException In case the backend does not exist
|
||||
*/
|
||||
public function edit($name, array $values)
|
||||
{
|
||||
if (! $name) {
|
||||
throw new InvalidArgumentException(t('Old authentication backend name missing'));
|
||||
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
|
||||
throw new InvalidArgumentException(t('New authentication backend name missing'));
|
||||
} elseif (($backendConfig = $this->config->get($name)) === null) {
|
||||
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
|
||||
}
|
||||
|
||||
if ($newName !== $name) {
|
||||
// Only remove the old entry if it has changed as the order gets screwed when editing backend names
|
||||
unset($this->config->{$name});
|
||||
}
|
||||
|
||||
unset($values['name']);
|
||||
$this->config->{$newName} = array_merge($backendConfig->toArray(), $values);
|
||||
return $this->config->{$newName};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given authentication backend
|
||||
*
|
||||
* @param string $name The name of the backend to remove
|
||||
*
|
||||
* @return array The removed backend configuration
|
||||
*
|
||||
* @throws InvalidArgumentException In case the backend does not exist
|
||||
*/
|
||||
public function remove($name)
|
||||
{
|
||||
if (! $name) {
|
||||
throw new InvalidArgumentException(t('Authentication backend name missing'));
|
||||
} elseif (($backendConfig = $this->config->get($name)) === null) {
|
||||
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
|
||||
}
|
||||
|
||||
unset($this->config->{$name});
|
||||
return $backendConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given authentication backend up or down in order
|
||||
*
|
||||
* @param string $name The name of the backend to be moved
|
||||
* @param int $position The new (absolute) position of the backend
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws InvalidArgumentException In case the backend does not exist
|
||||
*/
|
||||
public function move($name, $position)
|
||||
{
|
||||
if (! $name) {
|
||||
throw new InvalidArgumentException(t('Authentication backend name missing'));
|
||||
} elseif ($this->config->get($name) === null) {
|
||||
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
|
||||
}
|
||||
|
||||
$backendOrder = $this->config->keys();
|
||||
array_splice($backendOrder, array_search($name, $backendOrder), 1);
|
||||
array_splice($backendOrder, $position, 0, $name);
|
||||
|
||||
$newConfig = array();
|
||||
foreach ($backendOrder as $backendName) {
|
||||
$newConfig[$backendName] = $this->config->get($backendName);
|
||||
}
|
||||
|
||||
$config = new Config($newConfig);
|
||||
$this->config = $config->setConfigFile($this->config->getConfigFile());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or edit an authentication backend and save the configuration
|
||||
*
|
||||
* Performs a connectivity validation using the submitted values. A checkbox is
|
||||
* added to the form to skip the check if it fails and redirection is aborted.
|
||||
*
|
||||
* @see Form::onSuccess()
|
||||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
|
||||
$backendForm = $this->getBackendForm($this->getElement('type')->getValue());
|
||||
if (false === $backendForm->isValidAuthenticationBackend($this)) {
|
||||
$this->addForceCreationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$authBackend = $request->getQuery('auth_backend');
|
||||
try {
|
||||
if ($authBackend === null) { // create new backend
|
||||
$this->add($this->getValues());
|
||||
$message = t('Authentication backend "%s" has been successfully created');
|
||||
} else { // edit existing backend
|
||||
$this->edit($authBackend, $this->getValues());
|
||||
$message = t('Authentication backend "%s" has been successfully changed');
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {
|
||||
Notification::error($e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->save()) {
|
||||
Notification::success(sprintf($message, $this->getElement('name')->getValue()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the form in case an authentication backend is being edited
|
||||
*
|
||||
* @see Form::onShow()
|
||||
*
|
||||
* @throws ConfigurationError In case the backend name is missing in the request or is invalid
|
||||
*/
|
||||
public function onShow(Request $request)
|
||||
{
|
||||
$authBackend = $request->getQuery('auth_backend');
|
||||
if ($authBackend !== null) {
|
||||
if ($authBackend === '') {
|
||||
throw new ConfigurationError(t('Authentication backend name missing'));
|
||||
} elseif (false === isset($this->config->{$authBackend})) {
|
||||
throw new ConfigurationError(t('Unknown authentication backend provided'));
|
||||
} elseif (false === isset($this->config->{$authBackend}->backend)) {
|
||||
throw new ConfigurationError(sprintf(t('Backend "%s" has no `backend\' setting'), $authBackend));
|
||||
}
|
||||
|
||||
$configValues = $this->config->{$authBackend}->toArray();
|
||||
$configValues['type'] = $configValues['backend'];
|
||||
$configValues['name'] = $authBackend;
|
||||
$this->populate($configValues);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to be displayed at the beginning of the form
|
||||
* which allows the user to skip the connection validation
|
||||
*/
|
||||
protected function addForceCreationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'force_creation',
|
||||
array(
|
||||
'order' => 0,
|
||||
'ignore' => true,
|
||||
'label' => t('Force Changes'),
|
||||
'helptext' => t('Check this box to enforce changes without connectivity validation')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$backendTypes = array();
|
||||
$backendType = isset($formData['type']) ? $formData['type'] : 'db';
|
||||
|
||||
if (isset($this->resources['db'])) {
|
||||
$backendTypes['db'] = t('Database');
|
||||
}
|
||||
if (isset($this->resources['ldap']) && ($backendType === 'ldap' || ResourceFactory::ldapAvailable())) {
|
||||
$backendTypes['ldap'] = 'LDAP';
|
||||
}
|
||||
|
||||
$autologinBackends = array_filter(
|
||||
$this->config->toArray(),
|
||||
function ($authBackendCfg) {
|
||||
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
|
||||
}
|
||||
);
|
||||
if ($backendType === 'autologin' || empty($autologinBackends)) {
|
||||
$backendTypes['autologin'] = t('Autologin');
|
||||
}
|
||||
|
||||
$typeSelection = $this->createElement(
|
||||
'select',
|
||||
'type',
|
||||
array(
|
||||
'ignore' => true,
|
||||
'required' => true,
|
||||
'autosubmit' => true,
|
||||
'label' => t('Backend Type'),
|
||||
'helptext' => t('The type of the resource to use for this authenticaton backend'),
|
||||
'multiOptions' => $backendTypes
|
||||
)
|
||||
);
|
||||
|
||||
return array_merge(
|
||||
array($typeSelection),
|
||||
$this->getBackendForm($backendType)->createElements($formData)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Form\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Form\ConfigForm;
|
||||
|
||||
class AuthenticationBackendReorderForm extends ConfigForm
|
||||
{
|
||||
/**
|
||||
* Initialize this form
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_reorder_authbackend');
|
||||
$this->setViewScript('form/reorder-authbackend.phtml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ordered backend names
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBackendOrder()
|
||||
{
|
||||
return $this->config->keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the authentication backend order and save the configuration
|
||||
*
|
||||
* @see Form::onSuccess()
|
||||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
$formData = $this->getRequestData($request);
|
||||
if (isset($formData['backend_newpos'])) {
|
||||
$configForm = $this->getConfigForm();
|
||||
list($backendName, $position) = explode('|', $formData['backend_newpos'], 2);
|
||||
|
||||
try {
|
||||
if ($configForm->move($backendName, $position)->save()) {
|
||||
Notification::success(t('Authentication order updated!'));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {
|
||||
Notification::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the config form for authentication backends
|
||||
*
|
||||
* @return ConfigForm
|
||||
*/
|
||||
protected function getConfigForm()
|
||||
{
|
||||
$form = new AuthenticationBackendConfigForm();
|
||||
$form->setConfig($this->config);
|
||||
return $form;
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
<div class="controls">
|
||||
<?= $tabs; ?>
|
||||
</div>
|
||||
<div class="content" data-base-target="_next">
|
||||
<?= $messageBox; ?>
|
||||
<p>
|
||||
<a href="<?= $this->href('/config/createAuthenticationBackend', array('type' => 'ldap')); ?>">
|
||||
<?= $this->icon('create.png'); ?><?= $this->translate('Create A New LDAP Authentication Backend'); ?>
|
||||
</a>
|
||||
<br>
|
||||
<a href="<?= $this->href('/config/createAuthenticationBackend', array('type' => 'db')); ?>">
|
||||
<?= $this->icon('create.png'); ?><?= $this->translate('Create A New DB Authentication Backend'); ?>
|
||||
</a>
|
||||
<br>
|
||||
<a href="<?= $this->href('/config/createAuthenticationBackend', array('type' => 'autologin')); ?>">
|
||||
<?= $this->icon('create.png'); ?><?= $this->translate('Create A New Autologin Authentication Backend'); ?>
|
||||
</a>
|
||||
</p>
|
||||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>">
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<table class="action">
|
||||
<thead>
|
||||
<th>Backend</th>
|
||||
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
|
||||
<th style="width: 5em"><?= $this->translate('Order'); ?></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php for ($i = 0; $i < count($backendNames); $i++): ?>
|
||||
<tr>
|
||||
<td class="action">
|
||||
<a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backendNames[$i])); ?>">
|
||||
<?= $this->icon('edit.png'); ?> <?= $this->escape($backendNames[$i]); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backendNames[$i])); ?>">
|
||||
<?= $this->icon('remove.png', $this->translate('Remove')); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($i > 0): ?>
|
||||
<button type="submit" name="<?= $backendNames[$i]; ?>" value="<?= $i - 1; ?>">
|
||||
<?= $this->icon('up.png', $this->translate('Move up in authentication order')); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($i + 1 < count($backendNames)): ?>
|
||||
<button type="submit" name="<?= $backendNames[$i]; ?>" value="<?= $i + 1; ?>">
|
||||
<?= $this->icon('down.png', $this->translate('Move down in authentication order')); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endfor; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
|
@ -1,15 +1,7 @@
|
|||
<h4>
|
||||
<i class="icinga-icon-create"></i>
|
||||
Create New Authentication Backend
|
||||
</h4>
|
||||
|
||||
|
||||
<?php if (isset($this->messageBox)): ?>
|
||||
<?= $this->messageBox->render() ?>
|
||||
<?php endif ?>
|
||||
|
||||
<h4><?= $this->translate('Create New Authentication Backend'); ?></h4>
|
||||
<p>
|
||||
Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.
|
||||
<?= $this->translate(
|
||||
'Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.'
|
||||
); ?>
|
||||
</p>
|
||||
|
||||
<?= $this->form ?>
|
||||
<?= $form; ?>
|
|
@ -1,8 +1,2 @@
|
|||
<h4>
|
||||
<?php printf(
|
||||
$this->translate('Edit Backend "%s"'),
|
||||
$this->escape($this->name)
|
||||
); ?>
|
||||
</h4>
|
||||
<?= $messageBox; ?>
|
||||
<?= $this->form; ?>
|
||||
<h4><?= $this->translate('Edit Backend'); ?></h4>
|
||||
<?= $form; ?>
|
|
@ -1,8 +1,2 @@
|
|||
<h4>
|
||||
<?php printf(
|
||||
$this->translate('Remove Backend "%s"'),
|
||||
$this->escape($name)
|
||||
); ?>
|
||||
</h4>
|
||||
<?= $messageBox ?>
|
||||
<?= $form ?>
|
||||
<h4><?= $this->translate('Remove Backend'); ?></h4>
|
||||
<?= $form; ?>
|
|
@ -0,0 +1,11 @@
|
|||
<div class="controls">
|
||||
<?= $tabs; ?>
|
||||
</div>
|
||||
<div class="content" data-base-target="_next">
|
||||
<p>
|
||||
<a href="<?= $this->href('/config/createAuthenticationBackend'); ?>">
|
||||
<?= $this->icon('create.png'); ?><?= $this->translate('Create A New Authentication Backend'); ?>
|
||||
</a>
|
||||
</p>
|
||||
<?= $form; ?>
|
||||
</div>
|
|
@ -0,0 +1,40 @@
|
|||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>">
|
||||
<table class="action">
|
||||
<thead>
|
||||
<th>Backend</th>
|
||||
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
|
||||
<th style="width: 5em"><?= $this->translate('Order'); ?></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $backendNames = $form->getBackendOrder(); ?>
|
||||
<?php for ($i = 0; $i < count($backendNames); $i++): ?>
|
||||
<tr>
|
||||
<td class="action">
|
||||
<a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backendNames[$i])); ?>">
|
||||
<?= $this->icon('edit.png'); ?> <?= $this->escape($backendNames[$i]); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backendNames[$i])); ?>">
|
||||
<?= $this->icon('remove.png', $this->translate('Remove')); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($i > 0): ?>
|
||||
<button type="submit" name="backend_newpos" value="<?= sprintf('%s|%s', $backendNames[$i], $i - 1); ?>">
|
||||
<?= $this->icon('up.png', $this->translate('Move up in authentication order')); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($i + 1 < count($backendNames)): ?>
|
||||
<button type="submit" name="backend_newpos" value="<?= sprintf('%s|%s', $backendNames[$i], $i + 1); ?>">
|
||||
<?= $this->icon('down.png', $this->translate('Move down in authentication order')); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endfor; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
</form>
|
Loading…
Reference in New Issue