Fix Ldap server discovery
Add a new connection member that stores whether settings were guessed or product of a discovery, move discovery methods into seperate class. fixes #7691
This commit is contained in:
parent
cbcea2a3c3
commit
5f8fcf4005
|
@ -1,4 +1,6 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Forms;
|
||||
|
||||
|
@ -9,39 +11,8 @@ use Icinga\Protocol\Ldap\Connection;
|
|||
use Icinga\Protocol\Dns;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Form class for application-wide and logging specific settings
|
||||
*/
|
||||
class LdapDiscoveryForm extends Form
|
||||
{
|
||||
/**
|
||||
* The discovered server settings
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $capabilities = null;
|
||||
|
||||
/**
|
||||
* The discovered root_dn
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $namingContext = null;
|
||||
|
||||
/**
|
||||
* The working domain name
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $domain = null;
|
||||
|
||||
/**
|
||||
* The working port name
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $port = 389;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
|
@ -104,108 +75,6 @@ class LdapDiscoveryForm extends Form
|
|||
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 ($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 ($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 $this->capabilities->msCapabilities->ActiveDirectoryOid;
|
||||
}
|
||||
}
|
|
@ -94,6 +94,7 @@ class Connection
|
|||
|
||||
protected $capabilities;
|
||||
protected $namingContexts;
|
||||
protected $discoverySuccess = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -111,6 +112,16 @@ class Connection
|
|||
$this->port = $config->get('port', $this->port);
|
||||
}
|
||||
|
||||
public function getHostname()
|
||||
{
|
||||
return $this->hostname;
|
||||
}
|
||||
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
public function getDN()
|
||||
{
|
||||
return $this->root_dn;
|
||||
|
@ -390,6 +401,7 @@ class Connection
|
|||
try {
|
||||
$capabilities = $this->discoverCapabilities($ds);
|
||||
list($cap, $namingContexts) = $capabilities;
|
||||
$this->discoverySuccess = true;
|
||||
} catch (LdapException $e) {
|
||||
|
||||
// discovery failed, guess defaults
|
||||
|
@ -601,6 +613,17 @@ class Connection
|
|||
return $this->namingContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether service discovery was successful
|
||||
*
|
||||
* @return boolean True when ldap settings were discovered, false when
|
||||
* settings were guessed
|
||||
*/
|
||||
public function discoverySuccessful()
|
||||
{
|
||||
return $this->discoverySuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover the capabilities of the given ldap-server
|
||||
*
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Protocol\Ldap;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Protocol\Dns;
|
||||
|
||||
class Discovery {
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* If discovery was already performed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $discovered = false;
|
||||
|
||||
/**
|
||||
* @param Connection $conn The ldap connection to use for the discovery
|
||||
*/
|
||||
public function __construct(Connection $conn)
|
||||
{
|
||||
$this->connection = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the discovery on the underlying connection
|
||||
*/
|
||||
private function execDiscovery()
|
||||
{
|
||||
if (! $this->discovered) {
|
||||
$this->connection->connect();
|
||||
$this->discovered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests a resource configuration of hostname, port and root_dn
|
||||
* based on the discovery
|
||||
*
|
||||
* @return array The suggested configuration as an array
|
||||
*/
|
||||
public function suggestResourceSettings()
|
||||
{
|
||||
if (! $this->discovered) {
|
||||
$this->execDiscovery();
|
||||
}
|
||||
|
||||
return array(
|
||||
'hostname' => $this->connection->getHostname(),
|
||||
'port' => $this->connection->getPort(),
|
||||
'root_dn' => $this->connection->getDefaultNamingContext()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests a backend configuration of base_dn, user_class and user_name_attribute
|
||||
* based on the discovery
|
||||
*
|
||||
* @return array The suggested configuration as an array
|
||||
*/
|
||||
public function suggestBackendSettings()
|
||||
{
|
||||
$this->execDiscovery();
|
||||
if ($this->isAd()) {
|
||||
return array(
|
||||
'base_dn' => $this->connection->getDefaultNamingContext(),
|
||||
'user_class' => 'user',
|
||||
'user_name_attribute' => 'sAMAccountName'
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'base_dn' => $this->connection->getDefaultNamingContext(),
|
||||
'user_class' => 'getDefaultNamingContext',
|
||||
'user_name_attribute' => 'uid'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the suggested ldap server is an ActiveDirectory
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAd()
|
||||
{
|
||||
$this->execDiscovery();
|
||||
$caps = $this->connection->getCapabilities();
|
||||
return isset($caps->msCapabilities->ActiveDirectoryOid) && $caps->msCapabilities->ActiveDirectoryOid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the discovery was successful
|
||||
*
|
||||
* @return bool False when the suggestions are guessed
|
||||
*/
|
||||
public function isSuccess()
|
||||
{
|
||||
$this->execDiscovery();
|
||||
return $this->connection->discoverySuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover LDAP servers on the given domain
|
||||
*
|
||||
* @param string $domain The object containing the form elements
|
||||
*
|
||||
* @return Discovery True when the discovery was successful, false when the configuration was guessed
|
||||
*/
|
||||
public static function discoverDomain($domain)
|
||||
{
|
||||
if (! isset($domain)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt 1: Connect to the domain directly
|
||||
$disc = Discovery::discover($domain, 389);
|
||||
if ($disc->isSuccess()) {
|
||||
return $disc;
|
||||
}
|
||||
|
||||
// Attempt 2: Discover all available ldap dns records and connect to the first one
|
||||
$records = array_merge(Dns::getSrvRecords($domain, 'ldap'), Dns::getSrvRecords($domain, 'ldaps'));
|
||||
if (isset($records[0])) {
|
||||
$record = $records[0];
|
||||
return Discovery::discover(
|
||||
isset($record['target']) ? $record['target'] : $domain,
|
||||
isset($record['port']) ? $record['port'] : $domain
|
||||
);
|
||||
}
|
||||
|
||||
// Return the first failed discovery, which will suggest properties based on guesses
|
||||
return $disc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to instantiate a new Discovery
|
||||
*
|
||||
* @param $host The host on which to execute the discovery
|
||||
* @param $port The port on which to execute the discovery
|
||||
*
|
||||
* @return Discover The resulting Discovery
|
||||
*/
|
||||
public static function discover($host, $port)
|
||||
{
|
||||
$conn = new Connection(new Config(array(
|
||||
'hostname' => $host,
|
||||
'port' => $port
|
||||
)));
|
||||
return new Discovery($conn);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ namespace Icinga\Module\Setup\Forms;
|
|||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Forms\LdapDiscoveryForm;
|
||||
use Icinga\Protocol\Ldap\Discovery;
|
||||
use Icinga\Module\Setup\Forms\LdapDiscoveryConfirmPage;
|
||||
|
||||
/**
|
||||
* Wizard page to define the connection details for a LDAP resource
|
||||
|
@ -13,9 +15,9 @@ use Icinga\Forms\LdapDiscoveryForm;
|
|||
class LdapDiscoveryPage extends Form
|
||||
{
|
||||
/**
|
||||
* @var LdapDiscoveryForm
|
||||
* @var Discovery
|
||||
*/
|
||||
private $discoveryForm;
|
||||
private $discovery;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
|
@ -53,8 +55,8 @@ class LdapDiscoveryPage extends Form
|
|||
)
|
||||
);
|
||||
|
||||
$this->discoveryForm = new LdapDiscoveryForm();
|
||||
$this->addElements($this->discoveryForm->createElements($formData)->getElements());
|
||||
$discoveryForm = new LdapDiscoveryForm();
|
||||
$this->addElements($discoveryForm->createElements($formData)->getElements());
|
||||
$this->getElement('domain')->setRequired(
|
||||
isset($formData['skip_validation']) === false || ! $formData['skip_validation']
|
||||
);
|
||||
|
@ -82,25 +84,38 @@ class LdapDiscoveryPage extends Form
|
|||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $data['skip_validation'] && false === $this->discoveryForm->isValid($data)) {
|
||||
return false;
|
||||
if ($data['skip_validation']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (isset($data['domain'])) {
|
||||
$this->discovery = Discovery::discoverDomain($data['domain']);
|
||||
if ($this->discovery->isSuccess()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$this->addError(sprintf(t('Could not find any LDAP servers on the domain "%s".'), $data['domain']));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggest settings based on the underlying discovery
|
||||
*
|
||||
* @param bool $suppressArrayNotation
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getValues($suppressArrayNotation = false)
|
||||
{
|
||||
if (! isset($this->discoveryForm) || ! $this->discoveryForm->hasSuggestion()) {
|
||||
if (! isset($this->discovery) || ! $this->discovery->isSuccess()) {
|
||||
return null;
|
||||
}
|
||||
$disc = $this->discovery;
|
||||
return array(
|
||||
'domain' => $this->getValue('domain'),
|
||||
'type' => $this->discoveryForm->isAd() ?
|
||||
LdapDiscoveryConfirmPage::TYPE_AD : LdapDiscoveryConfirmPage::TYPE_MISC,
|
||||
'resource' => $this->discoveryForm->suggestResourceSettings(),
|
||||
'backend' => $this->discoveryForm->suggestBackendSettings()
|
||||
'type' => $disc->isAd() ? LdapDiscoveryConfirmPage::TYPE_AD : LdapDiscoveryConfirmPage::TYPE_MISC,
|
||||
'resource' => $disc->suggestResourceSettings(),
|
||||
'backend' => $disc->suggestBackendSettings()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue