From e29a568bff28252131ab60bc24320389da4fe358 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 9 Oct 2014 10:20:07 +0200 Subject: [PATCH] Add LDAP discovery pages refs #7163 --- application/forms/LdapDiscoveryForm.php | 214 ++++++++++++++++++ application/forms/Setup/AuthBackendPage.php | 24 ++ .../forms/Setup/LdapDiscoveryConfirmPage.php | 128 +++++++++++ application/forms/Setup/LdapDiscoveryPage.php | 97 ++++++++ application/forms/Setup/LdapResourcePage.php | 24 ++ library/Icinga/Application/WebSetup.php | 19 ++ 6 files changed, 506 insertions(+) create mode 100644 application/forms/LdapDiscoveryForm.php create mode 100644 application/forms/Setup/LdapDiscoveryConfirmPage.php create mode 100644 application/forms/Setup/LdapDiscoveryPage.php diff --git a/application/forms/LdapDiscoveryForm.php b/application/forms/LdapDiscoveryForm.php new file mode 100644 index 0000000000..47b6537af6 --- /dev/null +++ b/application/forms/LdapDiscoveryForm.php @@ -0,0 +1,214 @@ +setName('form_ldap_discovery'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'domain', + array( + 'required' => true, + 'label' => t('Search Domain'), + 'description' => t('Search this domain for records of available servers.'), + ) + ); + + if (false) { + $this->addElement( + new Note( + 'additional_description', + array( + 'value' => t('No Ldap servers found on this domain.' + . ' You can try to specify host and port and try again, or just skip this step and ' + . 'configure the server manually.' + ) + ) + ) + ); + $this->addElement( + 'text', + 'hostname', + array( + 'required' => false, + 'label' => t('Host'), + 'description' => t('IP or host name to search.'), + ) + ); + + $this->addElement( + 'text', + 'port', + array( + 'required' => false, + 'label' => t('Port'), + 'description' => t('Port', 389), + ) + ); + } + return $this; + } + + public function isValid($data) + { + if (false === parent::isValid($data)) { + return false; + } + if ($this->discover($this->getValue('domain'))) { + return true; + } + return true; + } + + + private function discover($domain) + { + // Attempt 1: Connect to the domain directly + if ($this->discoverCapabilities(array( + 'hostname' => $domain, + 'port' => 389) + )) { + return true; + } + + // Attempt 2: Discover all available ldap dns records and connect to the first one + $cap = false; + $records = array_merge(Dns::getSrvRecords($domain, 'ldap'), Dns::getSrvRecords($domain, 'ldaps')); + if (isset($records[0])) { + $record = $records[0]; + if (isset($record['port'])) { + $cap = $this->discoverCapabilities(array( + 'hostname' => $record['target'], + 'port' => $record['port'] + )); + } else { + $cap = $this->discoverCapabilities(array( + 'hostname' => $record['target'], + 'port' => 389 + )); + } + } + return $cap; + } + + private function discoverCapabilities($config) + { + $conn = new Connection(new Config($config)); + try { + $conn->connect(); + $this->capabilities = $conn->getCapabilities(); + $this->namingContext = $conn->getDefaultNamingContext(); + $this->port = $config['port']; + $this->domain = $config['hostname']; + return true; + } catch (LdapException $e) { + Logger::info( + 'Ldap discovery for ' . $config['hostname'] . ':' . $config['port'] . ' failed: ' . $e->getMessage() + ); + return false; + } + } + + public function suggestResourceSettings() + { + if (! isset($this->capabilities)) { + return array(); + } + if (isset($this->capabilities->msCapabilities->ActiveDirectoryOid)) { + return array( + 'hostname' => $this->domain, + 'port' => $this->port, + 'root_dn' => $this->namingContext + ); + } else { + return array( + 'hostname' => $this->domain, + 'port' => $this->port, + 'root_dn' => $this->namingContext + ); + } + } + + public function hasSuggestion() + { + return isset($this->capabilities); + } + + public function suggestBackendSettings() + { + if (! isset($this->capabilities)) { + return array(); + } + if (isset($this->capabilities->msCapabilities->ActiveDirectoryOid)) { + return array( + 'base_dn' => $this->namingContext, + 'user_class' => 'user', + 'user_name_attribute' => 'sAMAccountName' + ); + } else { + return array( + 'base_dn' => $this->namingContext, + 'user_class' => 'inetOrgPerson', + 'user_name_attribute' => 'uid' + ); + } + } + + public function isAd() + { + return isset($this->capabilities->msCapabilities->ActiveDirectoryOid); + } +} \ No newline at end of file diff --git a/application/forms/Setup/AuthBackendPage.php b/application/forms/Setup/AuthBackendPage.php index 12d3e3f7d0..d7dfe5debe 100644 --- a/application/forms/Setup/AuthBackendPage.php +++ b/application/forms/Setup/AuthBackendPage.php @@ -23,6 +23,13 @@ class AuthBackendPage extends Form */ protected $config; + /** + * Suggested configuration settings + * + * @var array + */ + protected $suggestions; + /** * Initialize this page */ @@ -54,6 +61,19 @@ public function getResourceConfig() return new Zend_Config($this->config); } + /** + * Set suggested configuration settings + * + * @param array $suggestions + * + * @return self + */ + public function setSuggestions(array $suggestions) + { + $this->suggestions = $suggestions; + return $this; + } + /** * @see Form::createElements() */ @@ -85,6 +105,10 @@ public function createElements(array $formData) $backendForm = new DbBackendForm(); $backendForm->createElements($formData)->removeElement('resource'); } elseif ($this->config['type'] === 'ldap') { + if ($this->suggestions !== null) { + $formData += $this->suggestions; + } + $backendForm = new LdapBackendForm(); $backendForm->createElements($formData)->removeElement('resource'); } else { // $this->config['type'] === 'autologin' diff --git a/application/forms/Setup/LdapDiscoveryConfirmPage.php b/application/forms/Setup/LdapDiscoveryConfirmPage.php new file mode 100644 index 0000000000..2c0d148ce1 --- /dev/null +++ b/application/forms/Setup/LdapDiscoveryConfirmPage.php @@ -0,0 +1,128 @@ + + Found LDAP server on {domain} + +EOT; + + /** + * The previous configuration + * + * @var array + */ + private $config; + + /** + * Initialize this page + */ + public function init() + { + $this->setName('setup_ldap_discovery_confirm'); + } + + /** + * Set the resource configuration to use + * + * @param array $config + * + * @return self + */ + public function setResourceConfig(array $config) + { + $this->config = $config; + return $this; + } + + /** + * Return the resource configuration as Zend_Config object + * + * @return Zend_Config + */ + public function getResourceConfig() + { + return new Zend_Config($this->config); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $resource = $this->config['resource']; + $backend = $this->config['backend']; + $html = $this->infoTemplate; + $html = str_replace('{domain}', $this->config['domain'], $html); + $html = str_replace('{type}', $this->config['type'], $html); + $html = str_replace('{hostname}', $resource['hostname'], $html); + $html = str_replace('{port}', $resource['port'], $html); + $html = str_replace('{root_dn}', $resource['root_dn'], $html); + $html = str_replace('{user_attribute}', $backend['user_name_attribute'], $html); + $html = str_replace('{user_class}', $backend['user_class'], $html); + + $this->addElement( + new Note( + 'suggestion', + array('value' => $html) + ) + ); + + $this->addElement( + 'checkbox', + 'confirm', + array( + 'value' => '1', + 'label' => t('Use this configuration?') + ) + ); + } + + /** + * Validate the given form data and check whether a BIND-request is successful + * + * @param array $data The data to validate + * + * @return bool + */ + public function isValid($data) + { + if (false === parent::isValid($data)) { + return false; + } + return true; + } + + public function getValues($suppressArrayNotation = false) + { + if ($this->getValue('confirm') === '1') { + // use configuration + return $this->config; + } + return null; + } +} diff --git a/application/forms/Setup/LdapDiscoveryPage.php b/application/forms/Setup/LdapDiscoveryPage.php new file mode 100644 index 0000000000..db052d03fc --- /dev/null +++ b/application/forms/Setup/LdapDiscoveryPage.php @@ -0,0 +1,97 @@ +setName('setup_ldap_discovery'); + } + + /** + * @see Form::createElements() + */ + public function createElements(array $formData) + { + $this->addElement( + new Note( + 'description', + array( + 'value' => t( + 'You can use this page to discover LDAP or ActiveDirectory servers ' . + ' for authentication. If you don\' want to execute a discovery, just skip this step.' + ) + ) + ) + ); + + $this->discoveryForm = new LdapDiscoveryForm(); + $this->addElements($this->discoveryForm->createElements($formData)->getElements()); + + $this->addElement( + 'checkbox', + 'skip_validation', + array( + 'required' => true, + 'label' => t('Skip'), + 'description' => t('Do not discover LDAP servers and enter all settings manually.') + ) + ); + } + + /** + * Validate the given form data and check whether a BIND-request is successful + * + * @param array $data The data to validate + * + * @return bool + */ + public function isValid($data) + { + if ($data['skip_validation'] === '1') { + return true; + } + if (false === parent::isValid($data)) { + return false; + } + if (false === $this->discoveryForm->isValid($data)) { + return false; + } + return true; + } + + public function getValues($suppressArrayNotation = false) + { + if (! isset($this->discoveryForm) || ! $this->discoveryForm->hasSuggestion()) { + return null; + } + return array( + 'domain' => $this->getValue('domain'), + 'type' => $this->discoveryForm->isAd() ? + LdapDiscoveryConfirmPage::TYPE_AD : LdapDiscoveryConfirmPage::TYPE_MISC, + 'resource' => $this->discoveryForm->suggestResourceSettings(), + 'backend' => $this->discoveryForm->suggestBackendSettings() + ); + } +} diff --git a/application/forms/Setup/LdapResourcePage.php b/application/forms/Setup/LdapResourcePage.php index 4a31502db0..9436743e3f 100644 --- a/application/forms/Setup/LdapResourcePage.php +++ b/application/forms/Setup/LdapResourcePage.php @@ -13,6 +13,13 @@ */ class LdapResourcePage extends Form { + /** + * Suggested configuration settings + * + * @var array + */ + protected $suggestions; + /** * Initialize this page */ @@ -21,6 +28,19 @@ public function init() $this->setName('setup_ldap_resource'); } + /** + * Set suggested configuration settings + * + * @param array $suggestions + * + * @return self + */ + public function setSuggestions(array $suggestions) + { + $this->suggestions = $suggestions; + return $this; + } + /** * @see Form::createElements() */ @@ -59,6 +79,10 @@ public function createElements(array $formData) ); } + if ($this->suggestions !== null) { + $formData += $this->suggestions; + } + $resourceForm = new LdapResourceForm(); $this->addElements($resourceForm->createElements($formData)->getElements()); } diff --git a/library/Icinga/Application/WebSetup.php b/library/Icinga/Application/WebSetup.php index a78a61a474..18139d71ae 100644 --- a/library/Icinga/Application/WebSetup.php +++ b/library/Icinga/Application/WebSetup.php @@ -11,6 +11,8 @@ use Icinga\Form\Setup\PreferencesPage; use Icinga\Form\Setup\AuthBackendPage; use Icinga\Form\Setup\AdminAccountPage; +use Icinga\Form\Setup\LdapDiscoveryPage; +use Icinga\Form\Setup\LdapDiscoveryConfirmPage; use Icinga\Form\Setup\LdapResourcePage; use Icinga\Form\Setup\RequirementsPage; use Icinga\Form\Setup\GeneralConfigPage; @@ -56,6 +58,8 @@ protected function init() $this->addPage(new AuthenticationPage()); $this->addPage(new PreferencesPage()); $this->addPage(new DbResourcePage()); + $this->addPage(new LdapDiscoveryPage()); + $this->addPage(new LdapDiscoveryConfirmPage()); $this->addPage(new LdapResourcePage()); $this->addPage(new AuthBackendPage()); $this->addPage(new AdminAccountPage()); @@ -82,7 +86,15 @@ public function setupPage(Form $page, Request $request) $page->setResourceConfig($this->getPageData('setup_db_resource')); } elseif ($authData['type'] === 'ldap') { $page->setResourceConfig($this->getPageData('setup_ldap_resource')); + + $suggestions = $this->getPageData('setup_ldap_discovery_confirm'); + if (isset($suggestions['backend'])) { + $page->setSuggestions($suggestions['backend']); + } } + } else if ($page->getName() === 'setup_ldap_discovery_confirm') { + $page->setResourceConfig($this->getPageData('setup_ldap_discovery')); + } elseif ($page->getName() === 'setup_admin_account') { $page->setBackendConfig($this->getPageData('setup_authentication_backend')); $authData = $this->getPageData('setup_authentication_type'); @@ -108,6 +120,11 @@ public function setupPage(Form $page, Request $request) t('The given resource name must be unique and is already in use by the database resource') ); } + $suggestion = $this->getPageData('setup_ldap_discovery_confirm'); + if (isset($suggestion['resource'])) { + $page->setSuggestions($suggestion['resource']); + } + } elseif ($page->getName() === 'setup_authentication_type') { $authData = $this->getPageData($page->getName()); if ($authData !== null && $request->getPost('type') !== $authData['type']) { @@ -131,6 +148,8 @@ protected function getNewPage($requestedPage, Form $originPage) $prefData = $this->getPageData('setup_preferences_type'); $authData = $this->getPageData('setup_authentication_type'); $skip = $prefData['type'] !== 'db' && $authData['type'] !== 'db'; + } elseif ($newPage->getName() === 'setup_ldap_discovery_confirm') { + $skip = $this->getPageData('setup_ldap_discovery') === null; } elseif ($newPage->getName() === 'setup_ldap_resource') { $authData = $this->getPageData('setup_authentication_type'); $skip = $authData['type'] !== 'ldap';