Merge branch 'master' into bugfix/drop-zend-config-7147

Conflicts:
	application/forms/LdapDiscoveryForm.php
This commit is contained in:
Johannes Meyer 2014-11-18 13:21:18 +01:00
commit c288a68ed5
19 changed files with 288 additions and 185 deletions

View File

@ -602,14 +602,14 @@ exec { 'create-pgsql-icingaweb-db':
}
exec { 'populate-icingaweb-mysql-db-tables':
unless => 'mysql -uicingaweb -picingaweb icingaweb -e "SELECT * FROM account;" &> /dev/null',
command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/mysql.sql',
unless => 'mysql -uicingaweb -picingaweb icingaweb -e "SELECT * FROM icingaweb_group;" &> /dev/null',
command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/mysql.schema.sql',
require => [ Exec['create-mysql-icingaweb-db'] ]
}
exec { 'populate-icingweba-pgsql-db-tables':
unless => 'psql -U icingaweb -d icingaweb -c "SELECT * FROM account;" &> /dev/null',
command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/pgsql.sql',
unless => 'psql -U icingaweb -d icingaweb -c "SELECT * FROM icingaweb_group;" &> /dev/null',
command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/pgsql.schema.sql',
require => [ Exec['create-pgsql-icingaweb-db'] ]
}

1
Vagrantfile vendored
View File

@ -54,7 +54,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
config.vm.synced_folder "./config", "/vagrant/config"
config.vm.synced_folder "./var/log", "/vagrant/var/log"
# Provider-specific configuration so you can fine-tune various

View File

@ -1,47 +1,13 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Forms;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Protocol\Ldap\Exception as LdapException;
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 +70,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 ConfigObject($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;
}
}
}

View File

@ -1,5 +1,4 @@
#!/usr/bin/php
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}

View File

@ -76,6 +76,7 @@ CREATE UNIQUE INDEX idx_icingaweb_user
CREATE TABLE "icingaweb_user_preference" (
"username" character varying(64) NOT NULL,
"name" character varying(64) NOT NULL,
"section" character varying(64) NOT NULL,
"value" character varying(255) NOT NULL,
"ctime" timestamp NULL DEFAULT NULL,
"mtime" timestamp NULL DEFAULT NULL
@ -85,6 +86,7 @@ ALTER TABLE ONLY "icingaweb_user_preference"
ADD CONSTRAINT pk_icingaweb_user_preference
PRIMARY KEY (
"username",
"section",
"name"
);
@ -92,5 +94,6 @@ CREATE UNIQUE INDEX idx_icingaweb_user_preference
ON "icingaweb_user_preference"
USING btree (
lower((username)::text),
lower((section)::text),
lower((name)::text)
);

View File

@ -95,6 +95,7 @@ class Connection
protected $capabilities;
protected $namingContexts;
protected $discoverySuccess = false;
/**
* Constructor
@ -112,6 +113,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;
@ -391,6 +402,7 @@ class Connection
try {
$capabilities = $this->discoverCapabilities($ds);
list($cap, $namingContexts) = $capabilities;
$this->discoverySuccess = true;
} catch (LdapException $e) {
// discovery failed, guess defaults
@ -602,6 +614,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
*

View File

@ -0,0 +1,158 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Ldap;
use Icinga\Data\ConfigObject;
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 ConfigObject(array(
'hostname' => $host,
'port' => $port
)));
return new Discovery($conn);
}
}

View File

@ -428,7 +428,7 @@ class ActionController extends Zend_Controller_Action
}
if ($this->autorefreshInterval !== null) {
// $resp->setHeader('X-Icinga-Refresh', $this->autorefreshInterval);
$resp->setHeader('X-Icinga-Refresh', $this->autorefreshInterval);
}
}

View File

@ -11,7 +11,6 @@ use Zend_View_Interface;
use Icinga\Application\Icinga;
use Icinga\Web\Form\Decorator\NoScriptApply;
use Icinga\Web\Form\Element\CsrfCounterMeasure;
use Icinga\Web\Form\FormElement;
/**
* Base class for forms providing CSRF protection, confirmation logic and auto submission
@ -139,6 +138,21 @@ class Form extends Zend_Form
throw new LogicException('The option `onSuccess\' is not callable');
}
// Zend's plugin loader reverses the order of added prefix paths thus trying our paths first before trying
// Zend paths
$this->addPrefixPaths(array(
array(
'prefix' => 'Icinga\\Web\\Form\\Element\\',
'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Element'),
'type' => static::ELEMENT
),
array(
'prefix' => 'Icinga\\Web\\Form\\Decorator\\',
'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Decorator'),
'type' => static::DECORATOR
)
));
parent::__construct($options);
}
@ -464,9 +478,7 @@ class Form extends Zend_Form
$options = array('decorators' => static::$defaultElementDecorators);
}
if (($el = $this->createIcingaFormElement($type, $name, $options)) === null) {
$el = parent::createElement($type, $name, $options);
}
$el = parent::createElement($type, $name, $options);
if ($el && $el->getAttrib('autosubmit')) {
$noScript = new NoScriptApply(); // Non-JS environments
@ -738,25 +750,6 @@ class Form extends Zend_Form
return array();
}
/**
* Create a new element located in the Icinga Web 2 library
*
* @param string $type The type of the element
* @param string $name The name of the element
* @param mixed $options The options for the element
*
* @return NULL|FormElement NULL in case the element is not found in the Icinga Web 2 library
*
* @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators.
*/
protected function createIcingaFormElement($type, $name, $options = null)
{
$className = 'Icinga\\Web\\Form\\Element\\' . ucfirst($type);
if (class_exists($className)) {
return new $className($name, $options);
}
}
/**
* Render this form
*

View File

@ -47,7 +47,6 @@ class Number extends FormElement
*/
public function init()
{
$this->addValidator('Float', true); // true for breaking the validator chain on failure
if ($this->min !== null) {
$this->addValidator('GreaterThan', true, array('min' => $this->min));
}
@ -127,4 +126,19 @@ class Number extends FormElement
{
return $this->step;
}
/**
* (non-PHPDoc)
* @see \Zend_Form_Element::isValid() For the method documentation.
*/
public function isValid($value, $context = null)
{
$this->setValue($value);
$value = $this->getValue();
if (! is_numeric($value)) {
$this->addError(sprintf($this->translate('\'%s\' is not a valid number'), $value));
return false;
}
return parent::isValid($value, $context);
}
}

View File

@ -2,11 +2,19 @@
use Icinga\Application\Icinga;
use Icinga\Web\Controller;
use Icinga\Web\Widget;
class Doc_StyleController extends Controller
{
public function fontAction()
{
$this->view->tabs = Widget::create('tabs')->add(
'fonts',
array(
'title' => $this->translate('Icons'),
'url' => 'doc/style/font'
)
)->activate('fonts');
$confFile = Icinga::app()->getApplicationDir('fonts/fontanello-ifont/config.json');
$this->view->font = json_decode(file_get_contents($confFile));
}

View File

@ -0,0 +1,3 @@
<div class="chapter">
<?= $sectionRenderer->render($this, $this->getHelper('Url')); ?>
</div>

View File

@ -0,0 +1,6 @@
<div class="controls">
<h1><?= $title ?></h1>
</div>
<div class="content toc">
<?= $tocRenderer->render($this, $this->getHelper('Url')); ?>
</div>

View File

@ -1,4 +1,5 @@
<div class="controls">
<?= $this->tabs ?>
<h1>Icinga Web 2 Icons</h1>
</div>

View File

@ -17,7 +17,7 @@ $section->add('Icinga Web 2', array(
$section->add('Module documentations', array(
'url' => 'doc/module',
));
$section->add($this->translate('Fonts'), array(
$section->add($this->translate('Developer - Style'), array(
'url' => 'doc/style/font',
'priority' => 200,
));

View File

@ -7,11 +7,15 @@ if ($object->getType() === $object::TYPE_HOST) {
'monitoring/host/reschedule-check',
array('host' => $object->getName())
);
$checkAttempts = $object->host_current_check_attempt . '/' . $object->host_max_check_attempts;
$stateType = (int) $object->host_state_type;
} else {
$reschedule = $this->href(
'monitoring/service/reschedule-check',
array('host' => $object->getHost()->getName(), 'service' => $object->getName())
);
$checkAttempts = $object->service_attempt;
$stateType = (int) $object->service_state_type;
}
?>
@ -32,6 +36,14 @@ if ($object->getType() === $object::TYPE_HOST) {
<?= $this->timeUntil($object->next_check) ?>
</td>
</tr>
<tr>
<th><?= $this->translate('Check attempts') ?></th>
<?php if ($stateType === 0): ?>
<td><?= $checkAttempts ?> <?= $this->translate('(soft state)') ?></td>
<?php else: ?>
<td><?= $checkAttempts ?> <?= $this->translate('(hard state)') ?></td>
<?php endif; ?>
</tr>
<?php if ($object->check_execution_time): ?>
<tr>
<th><?= $this->translate('Check execution time') ?></th>

View File

@ -6,7 +6,12 @@ $currentUrl = Url::fromRequest()->without('limit')->getRelativeUrl();
?>
<h3 class="tinystatesummary" <?= $this->compact ? ' data-base-target="col1"' : '' ?>>
<?php if ($object->stats->services_total > 0): ?>
<?= $this->qlink(sprintf($this->translate('%s configured services:'), $object->stats->services_total), $selfUrl) ?>
<?php else: ?>
<?= $this->translate('No services configured on this host'); ?>
<?php endif; ?>
<?php if ($object->stats->services_ok > 0): ?>
<span class="state ok<?= $currentUrl === $selfUrl->with('service_state', 0)->getRelativeUrl() ? ' active' : '' ?>"><?= $this->qlink(
$object->stats->services_ok,

View File

@ -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()
);
}
}

View File

@ -8,7 +8,7 @@ ul.tabs {
.controls ul.tabs {
margin-left: 0em;
margin-top: -3.6em;
margin-top: -3.54em;
height: 2.6em;
overflow: hidden;
}