Merge branch 'master' into feature/display-documentation-4820

Conflicts:
	modules/doc/library/Doc/DocParser.php
This commit is contained in:
Eric Lippmann 2014-08-19 13:39:48 +02:00
commit 1c74fd7028
58 changed files with 770 additions and 393 deletions

3
.gitignore vendored
View File

@ -36,6 +36,9 @@ config/preferences/*.ini
# Application logfiles # Application logfiles
var/log/*.log var/log/*.log
# Packaging
/debian /debian
*.tar.gz
*.komodoproject

View File

@ -1,6 +1,6 @@
object CheckCommand "dummy-host" { object CheckCommand "dummy-host" {
import "plugin-check-command" import "plugin-check-command"
command = [ PluginDir + "/libexec/test_hostcheck.pl" ] command = [ PluginDir + "/test_hostcheck.pl" ]
arguments = { arguments = {
"--type" = "$check_type$" "--type" = "$check_type$"
"--failchance" = "$check_failchance$" "--failchance" = "$check_failchance$"
@ -18,7 +18,7 @@ object CheckCommand "dummy-host" {
object CheckCommand "dummy-service" { object CheckCommand "dummy-service" {
import "plugin-check-command" import "plugin-check-command"
command = [ PluginDir + "/libexec/test_servicecheck.pl" ] command = [ PluginDir + "/test_servicecheck.pl" ]
arguments = { arguments = {
"--total-critical-on-host" = "$check_critical_on_host$" "--total-critical-on-host" = "$check_critical_on_host$"
"--total-warning-on-host" = "$check_warning_on_host$" "--total-warning-on-host" = "$check_warning_on_host$"

View File

@ -18,10 +18,15 @@ title = "Configuration"
url = "config" url = "config"
priority = 300 priority = 300
[System.Modules]
title = "Modules"
url = "config/modules"
priority = 400
[System.ApplicationLog] [System.ApplicationLog]
title = "Application log" title = "Application log"
url = "list/applicationlog" url = "list/applicationlog"
priority = 400 priority = 500
[Logout] [Logout]
url = "authentication/logout" url = "authentication/logout"

View File

@ -3,10 +3,14 @@ include mysql
include pgsql include pgsql
include openldap include openldap
Exec { path => '/bin:/usr/bin:/sbin' } Exec { path => '/bin:/usr/bin:/sbin:/usr/sbin' }
$icingaVersion = '1.11.2' $icingaVersion = '1.11.5'
$icinga2Version = '2.0.0' $icinga2Version = '2.0.1'
$pluginVersion = '2.0'
$livestatusVersion = '1.2.4p5'
$phantomjsVersion = '1.9.1'
$casperjsVersion = '1.0.2'
exec { 'create-mysql-icinga-db': exec { 'create-mysql-icinga-db':
unless => 'mysql -uicinga -picinga icinga', unless => 'mysql -uicinga -picinga icinga',
@ -72,10 +76,10 @@ cmmi { 'icinga-mysql':
--with-htmurl=/icinga-mysql --with-httpd-conf-file=/etc/httpd/conf.d/icinga-mysql.conf \ --with-htmurl=/icinga-mysql --with-httpd-conf-file=/etc/httpd/conf.d/icinga-mysql.conf \
--with-cgiurl=/icinga-mysql/cgi-bin \ --with-cgiurl=/icinga-mysql/cgi-bin \
--with-http-auth-file=/usr/share/icinga/htpasswd.users \ --with-http-auth-file=/usr/share/icinga/htpasswd.users \
--with-plugin-dir=/usr/lib64/nagios/plugins/libexec', --with-plugin-dir=/usr/lib64/nagios/plugins',
creates => '/usr/local/icinga-mysql', creates => '/usr/local/icinga-mysql',
make => 'make all && make fullinstall install-config', make => 'make all && make fullinstall install-config',
require => [ User['icinga'], Cmmi['icinga-plugins'], Package['apache'] ], require => [ User['icinga'], Class['monitoring-plugins'], Package['apache'] ],
notify => Service['apache'] notify => Service['apache']
} }
@ -98,10 +102,10 @@ cmmi { 'icinga-pgsql':
--with-htmurl=/icinga-pgsql --with-httpd-conf-file=/etc/httpd/conf.d/icinga-pgsql.conf \ --with-htmurl=/icinga-pgsql --with-httpd-conf-file=/etc/httpd/conf.d/icinga-pgsql.conf \
--with-cgiurl=/icinga-pgsql/cgi-bin \ --with-cgiurl=/icinga-pgsql/cgi-bin \
--with-http-auth-file=/usr/share/icinga/htpasswd.users \ --with-http-auth-file=/usr/share/icinga/htpasswd.users \
--with-plugin-dir=/usr/lib64/nagios/plugins/libexec', --with-plugin-dir=/usr/lib64/nagios/plugins',
creates => '/usr/local/icinga-pgsql', creates => '/usr/local/icinga-pgsql',
make => 'make all && make fullinstall install-config', make => 'make all && make fullinstall install-config',
require => [ User['icinga'], Cmmi['icinga-plugins'], Package['apache'] ], require => [ User['icinga'], Class['monitoring-plugins'], Package['apache'] ],
notify => Service['apache'] notify => Service['apache']
} }
@ -206,20 +210,11 @@ exec { 'icinga-htpasswd':
require => Class['apache'] require => Class['apache']
} }
cmmi { 'icinga-plugins': include monitoring-plugins
url => 'https://www.monitoring-plugins.org/download/nagios-plugins-1.5.tar.gz',
output => 'nagios-plugins-1.5.tar.gz',
flags => '--prefix=/usr/lib64/nagios/plugins \
--with-nagios-user=icinga --with-nagios-group=icinga \
--with-cgiurl=/icinga-mysql/cgi-bin',
creates => '/usr/lib64/nagios/plugins/libexec',
make => 'make && make install',
require => User['icinga']
}
cmmi { 'mk-livestatus': cmmi { 'mk-livestatus':
url => 'http://mathias-kettner.de/download/mk-livestatus-1.2.2p1.tar.gz', url => "http://mathias-kettner.de/download/mk-livestatus-${livestatusVersion}.tar.gz",
output => 'mk-livestatus-1.2.2p1.tar.gz', output => "mk-livestatus-${livestatusVersion}.tar.gz",
flags => '--prefix=/usr/local/icinga-mysql --exec-prefix=/usr/local/icinga-mysql', flags => '--prefix=/usr/local/icinga-mysql --exec-prefix=/usr/local/icinga-mysql',
creates => '/usr/local/icinga-mysql/lib/mk-livestatus', creates => '/usr/local/icinga-mysql/lib/mk-livestatus',
make => 'make && make install', make => 'make && make install',
@ -262,14 +257,14 @@ exec { 'populate-openldap':
} }
class { 'phantomjs': class { 'phantomjs':
url => 'https://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2', url => "https://phantomjs.googlecode.com/files/phantomjs-${phantomjsVersion}-linux-x86_64.tar.bz2",
output => 'phantomjs-1.9.1-linux-x86_64.tar.bz2', output => "phantomjs-${phantomjsVersion}-linux-x86_64.tar.bz2",
creates => '/usr/local/phantomjs' creates => '/usr/local/phantomjs'
} }
class { 'casperjs': class { 'casperjs':
url => 'https://github.com/n1k0/casperjs/tarball/1.0.2', url => "https://github.com/n1k0/casperjs/tarball/${casperjsVersion}",
output => 'casperjs-1.0.2.tar.gz', output => "casperjs-${casperjsVersion}.tar.gz",
creates => '/usr/local/casperjs' creates => '/usr/local/casperjs'
} }
@ -421,11 +416,10 @@ package { 'icinga2-ido-mysql':
exec { 'populate-icinga2-mysql-db': exec { 'populate-icinga2-mysql-db':
unless => 'mysql -uicinga2 -picinga2 icinga2 -e "SELECT * FROM icinga_dbversion;" &> /dev/null', unless => 'mysql -uicinga2 -picinga2 icinga2 -e "SELECT * FROM icinga_dbversion;" &> /dev/null',
command => "mysql -uroot icinga2 < /usr/share/doc/icinga2-ido-mysql-$icinga2Version/schema/mysql.sql", command => 'mysql -uroot icinga2 < /usr/share/icinga2-ido-mysql/schema/mysql.sql',
require => [ Exec['create-mysql-icinga2-db'], Package['icinga2-ido-mysql'] ] require => [ Exec['create-mysql-icinga2-db'], Package['icinga2-ido-mysql'] ]
} }
file { '/etc/icinga2/features-available/ido-mysql.conf': file { '/etc/icinga2/features-available/ido-mysql.conf':
source => 'puppet:////vagrant/.vagrant-puppet/files/etc/icinga2/features-available/ido-mysql.conf', source => 'puppet:////vagrant/.vagrant-puppet/files/etc/icinga2/features-available/ido-mysql.conf',
owner => 'icinga', owner => 'icinga',
@ -574,7 +568,7 @@ populate_monitoring_test_config { ['commands', 'contacts', 'dependencies',
} }
define populate_monitoring_test_config_plugins { define populate_monitoring_test_config_plugins {
file { "/usr/lib64/nagios/plugins/libexec/${name}": file { "/usr/lib64/nagios/plugins/${name}":
owner => 'icinga', owner => 'icinga',
group => 'icinga', group => 'icinga',
source => "/usr/local/share/misc/monitoring_test_config/plugins/${name}", source => "/usr/local/share/misc/monitoring_test_config/plugins/${name}",

View File

@ -0,0 +1,9 @@
class monitoring-plugins {
include epel
# nagios plugins from epel
package { 'nagios-plugins-all':
ensure => installed,
require => Class['epel']
}
}

View File

@ -2,13 +2,59 @@
## Table of Contents ## Table of Contents
1. [Vagrant - Virtual development environment](#vagrant) 0. [General Information](#general information)
1. [Installation](#installation)
2. [Support](#support)
3. [Vagrant - Virtual development environment](#vagrant)
## General Information
`Icinga Web 2` is the next generation monitoring web interface, framework
and CLI tool developed by the [Icinga Project](https://www.icinga.org/community/team/).
Responsive and fast, rewritten from scratch supporting multiple backends and
providing a CLI tool. Compatible with Icinga Core 2.x and 1.x.
Check the Icinga website for some [insights](https://www.icinga.org/icinga/screenshots/icinga-web-2/).
> **Note**
>
> `Icinga Web 2` is still in development and not meant for production deployment.
> Watch the [development roadmap](https://dev.icinga.org/projects/icingaweb2/roadmap)
> and [Icinga website](https://www.icinga.org/) for release schedule updates!
## Installation
Please navigate to [doc/installation.md](doc/installation.md) for updated details.
## Support
Please head over to the [community support channels](https://www.icinga.org/icinga/faq/get-help/)
in case of questions, bugs, etc.
Please make sure to provide the following details:
* OS, distribution, version
* PHP and/or MySQL/PostgreSQL version
* Which browser and its version
* Screenshot and problem description
## Vagrant ## Vagrant
> **Note** that the deployment of the virtual machine is tested against Vagrant starting with version 1.1. ### Requirements
* Vagrant 1.2+
* Virtualbox 4.2.16+
* a fairly powerful hardware (quad core, 4gb ram, fast hdd)
> **Note**
>
> The deployment of the virtual machine is tested against Vagrant starting with version 1.2.
> Unfortunately older versions will not work. > Unfortunately older versions will not work.
### General
The Icinga Web 2 project ships with a Vagrant virtual machine that integrates The Icinga Web 2 project ships with a Vagrant virtual machine that integrates
the source code with various services and example data in a controlled the source code with various services and example data in a controlled
environment. This enables developers and users to test Livestatus, status.dat, environment. This enables developers and users to test Livestatus, status.dat,
@ -17,7 +63,9 @@ have to do is install Vagrant and run:
vagrant up vagrant up
> **Note** that the first boot of the vm takes a fairly long time because > **Note**
>
> The first boot of the vm takes a fairly long time because
> you'll download a plain CentOS base box and Vagrant will automatically > you'll download a plain CentOS base box and Vagrant will automatically
> provision the environment on the first go. > provision the environment on the first go.
@ -68,7 +116,7 @@ After you should be able to browse [localhost:8080/icingaweb](http://localhost:8
**Installed files**: **Installed files**:
* `/usr/share/icinga/htpasswd.users` account information for logging into the Icinga classic web interface for both icinga instances * `/usr/share/icinga/htpasswd.users` account information for logging into the Icinga classic web interface for both icinga instances
* `/usr/lib64/nagios/plugins` Nagios Plugins for both icinga instances * `/usr/lib64/nagios/plugins` Monitoring Plugins for all Icinga instances
#### Icinga with IDOUtils using a MySQL database #### Icinga with IDOUtils using a MySQL database
@ -196,12 +244,13 @@ code style issues.
#### Icinga 2 #### Icinga 2
**Installation path**: `/usr/local/icinga2` Installed from the Icinga [snapshot package repository](http://packages.icinga.org/epel/).
The configuration is located in `/etc/icinga2`.
**Example usage**: **Example usage**:
cd /usr/local/icinga2 /etc/init.d/icinga2 (start|stop|restart|reload)
./sbin/icinga2 -c etc/icinga2/icinga2.conf.dist
## Log into Icinga Web 2 ## Log into Icinga Web 2
@ -211,3 +260,9 @@ If you've configure LDAP as authentication backend (which is the default) use th
> **Password**: password > **Password**: password
Have a look at [LDAP example data](#ldap example data) for more accounts. Have a look at [LDAP example data](#ldap example data) for more accounts.
Using MySQL as backend:
> **Username**: icingaadmin
> **Password**: icinga

View File

@ -14,6 +14,7 @@ use Icinga\Exception\AuthenticationException;
use Icinga\Exception\NotReadableError; use Icinga\Exception\NotReadableError;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\User; use Icinga\User;
use Icinga\Web\Session;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**
@ -34,12 +35,17 @@ class AuthenticationController extends ActionController
public function loginAction() public function loginAction()
{ {
$auth = $this->Auth(); $auth = $this->Auth();
$this->view->form = new LoginForm(); $this->view->form = $form = new LoginForm();
$this->view->form->setRequest($this->_request); $form->setRequest($this->_request);
$this->view->title = $this->translate('Icingaweb Login'); $this->view->title = $this->translate('Icingaweb Login');
try { try {
$redirectUrl = Url::fromPath($this->params->get('redirect', 'dashboard')); $redirectUrl = $this->view->form->getValue('redirect');
if ($redirectUrl) {
$redirectUrl = Url::fromPath($redirectUrl);
} else {
$redirectUrl = Url::fromPath('dashboard');
}
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {
$this->rerenderLayout()->redirectNow($redirectUrl); $this->rerenderLayout()->redirectNow($redirectUrl);
@ -71,12 +77,20 @@ class AuthenticationController extends ActionController
} }
} }
} }
} elseif ($this->view->form->isSubmittedAndValid()) { } elseif ($form->isSubmittedAndValid()) {
$user = new User($this->view->form->getValue('username')); $user = new User($form->getValue('username'));
$password = $this->view->form->getValue('password'); $password = $form->getValue('password');
$backendsTried = 0; $backendsTried = 0;
$backendsWithError = 0; $backendsWithError = 0;
$redirectUrl = $form->getValue('redirect');
if ($redirectUrl) {
$redirectUrl = Url::fromPath($redirectUrl);
} else {
$redirectUrl = Url::fromPath('dashboard');
}
foreach ($chain as $backend) { foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) { if ($backend instanceof AutoLoginBackend) {
continue; continue;
@ -111,14 +125,14 @@ class AuthenticationController extends ActionController
); );
} }
if ($backendsWithError) { if ($backendsWithError) {
$this->view->form->addNote( $form->addNote(
$this->translate( $this->translate(
'Note that not all authentication backends are available for authentication because they' 'Note that not all authentication backends are available for authentication because they'
. ' have errors. Please check the system log or Icinga Web 2 log for more information' . ' have errors. Please check the system log or Icinga Web 2 log for more information'
) )
); );
} }
$this->view->form->getElement('password')->addError($this->translate('Incorrect username or password')); $form->getElement('password')->addError($this->translate('Incorrect username or password'));
} }
} catch (Exception $e) { } catch (Exception $e) {
$this->view->errorInfo = $e->getMessage(); $this->view->errorInfo = $e->getMessage();
@ -131,9 +145,10 @@ class AuthenticationController extends ActionController
public function logoutAction() public function logoutAction()
{ {
$auth = $this->Auth(); $auth = $this->Auth();
$isRemoteUser = $auth->getUser()->isRemoteUser();
$auth->removeAuthorization(); $auth->removeAuthorization();
if ($auth->isAuthenticatedFromRemoteUser()) { if ($isRemoteUser === true) {
$this->_helper->layout->setLayout('login'); $this->_helper->layout->setLayout('login');
$this->_response->setHttpResponseCode(401); $this->_response->setHttpResponseCode(401);
} else { } else {

View File

@ -48,9 +48,6 @@ class ConfigController extends BaseConfigController
))->add('logging', array( ))->add('logging', array(
'title' => 'Logging', 'title' => 'Logging',
'url' => 'config/logging' 'url' => 'config/logging'
))->add('modules', array(
'title' => 'Modules',
'url' => 'config/modules'
)); ));
} }
@ -70,7 +67,7 @@ class ConfigController extends BaseConfigController
$form->setConfiguration(IcingaConfig::app()); $form->setConfiguration(IcingaConfig::app());
$form->setRequest($this->_request); $form->setRequest($this->_request);
if ($form->isSubmittedAndValid()) { if ($form->isSubmittedAndValid()) {
if (!$this->writeConfigFile($form->getConfig(), 'config')) { if (!$this->writeConfigFile($form->getConfig(), 'config')) {
return; return;
} }
Notification::success('New configuration has successfully been stored'); Notification::success('New configuration has successfully been stored');
@ -107,6 +104,11 @@ class ConfigController extends BaseConfigController
*/ */
public function modulesAction() public function modulesAction()
{ {
$this->view->tabs = Widget::create('tabs')->add('modules', array(
'title' => 'Modules',
'url' => 'config/modules'
));
$this->view->tabs->activate('modules'); $this->view->tabs->activate('modules');
$this->view->modules = Icinga::app()->getModuleManager()->select() $this->view->modules = Icinga::app()->getModuleManager()->select()
->from('modules') ->from('modules')

View File

@ -18,7 +18,9 @@ class LayoutController extends ActionController
*/ */
public function menuAction() public function menuAction()
{ {
$this->view->menuRenderer = new MenuRenderer(Menu::fromConfig()->order(), Url::fromRequest()->getRelativeUrl()); $this->view->menuRenderer = new MenuRenderer(
Menu::fromConfig()->order(), Url::fromRequest()->without('renderLayout')->getRelativeUrl()
);
} }
/** /**

View File

@ -5,6 +5,7 @@
namespace Icinga\Form\Authentication; namespace Icinga\Form\Authentication;
use Icinga\Web\Form; use Icinga\Web\Form;
use Icinga\Web\Url;
/** /**
* Class LoginForm * Class LoginForm
@ -16,12 +17,19 @@ class LoginForm extends Form
*/ */
protected function create() protected function create()
{ {
$url = Url::fromRequest()->without('renderLayout');
$this->setName('form_login'); $this->setName('form_login');
$this->addElement('text', 'username', array( $this->addElement('text', 'username', array(
'label' => t('Username'), 'label' => t('Username'),
'placeholder' => t('Please enter your username...'), 'placeholder' => t('Please enter your username...'),
'required' => true, 'required' => true,
)); ));
$redir = $this->addElement('hidden', 'redirect');
$redirectUrl = $url->shift('redirect');
if ($redirectUrl) {
$this->setDefault('redirect', $redirectUrl);
}
$this->addElement('password', 'password', array( $this->addElement('password', 'password', array(
'label' => t('Password'), 'label' => t('Password'),
@ -34,6 +42,7 @@ class LoginForm extends Form
} else { } else {
$this->getElement('username')->setAttrib('class', 'autofocus'); $this->getElement('username')->setAttrib('class', 'autofocus');
} }
$this->setAction((string) $url);
$this->setSubmitLabel('Login'); $this->setSubmitLabel('Login');
} }
} }

View File

@ -93,7 +93,6 @@ class GeneralForm extends Form
foreach (Translator::getAvailableLocaleCodes() as $language) { foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language; $languages[$language] = $language;
} }
$languages[Translator::DEFAULT_LOCALE] = Translator::DEFAULT_LOCALE;
$this->addElement( $this->addElement(
'select', 'select',

View File

@ -33,7 +33,6 @@ class GeneralForm extends Form
foreach (Translator::getAvailableLocaleCodes() as $language) { foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language; $languages[$language] = $language;
} }
$languages[Translator::DEFAULT_LOCALE] = Translator::DEFAULT_LOCALE;
$prefs = $this->getUserPreferences(); $prefs = $this->getUserPreferences();
$useDefaultLanguage = $this->getRequest()->getParam('default_language', !$prefs->has('app.language')); $useDefaultLanguage = $this->getRequest()->getParam('default_language', !$prefs->has('app.language'));

View File

@ -38,7 +38,7 @@ if ($notifications->hasMessages()) {
</div> </div>
<?php endif ?> <?php endif ?>
<div id="main" role="main"> <div id="main" role="main">
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest() ?>"<?= $refresh ?> style="display: block"> <div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest()->without('renderLayout') ?>"<?= $refresh ?> style="display: block">
<?= $this->render('inline.phtml') ?> <?= $this->render('inline.phtml') ?>
</div> </div>
<div id="col2" class="container"> <div id="col2" class="container">

View File

@ -44,6 +44,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script> <script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
<![endif]--> <![endif]-->
<link type="image/png" rel="shortcut icon" href="<?= $this->baseUrl('img/favicon.png') ?>" />
</head> </head>
<body id="body"> <body id="body">

View File

@ -14,5 +14,5 @@ if (! $this->auth()->isAuthenticated()) {
<form action="<?= $this->href('search') ?>" method="get" role="search"> <form action="<?= $this->href('search') ?>" method="get" role="search">
<input type="text" name="q" class="search autofocus" placeholder="<?= $this->translate('Search...') ?>" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /> <input type="text" name="q" class="search autofocus" placeholder="<?= $this->translate('Search...') ?>" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
</form> </form>
<?= new MenuRenderer(Menu::fromConfig()->order(), Url::fromRequest()->getRelativeUrl()); ?> <?= new MenuRenderer(Menu::fromConfig()->order(), Url::fromRequest()->without('renderLayout')->getRelativeUrl()); ?>
</div> </div>

View File

@ -7,10 +7,7 @@
in every further request until the browser was closed. To allow logout and to allow the user to change the in every further request until the browser was closed. To allow logout and to allow the user to change the
logged-in user this JavaScript provides a workaround to force a new authentication prompt in most browsers. logged-in user this JavaScript provides a workaround to force a new authentication prompt in most browsers.
--> -->
<div class="content">
<div class="row">
<br/>
<div class="md-offset-3 col-md-6 col-sm-6 col-sm-offset-3">
<div class="alert alert-warning" id="logout-status"> <div class="alert alert-warning" id="logout-status">
<b> <?= t('Logging out...'); ?> </b> <br /> <b> <?= t('Logging out...'); ?> </b> <br />
<?= t( <?= t(
@ -19,37 +16,19 @@
'browser session.' 'browser session.'
); ?> ); ?>
</div> </div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3">
<div class="container" > <div class="container" >
<a class="button btn btn-cta form-control input-sm" href="<?= $this->href('dashboard/index'); ?>"> <?= t('Login'); ?></a> <a href="<?= $this->href('dashboard/index'); ?>"> <?= t('Login'); ?></a>
</div> </div>
</div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
/** /**
* When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it * When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it
* before it is able to finish. This will cause the browser to show a new authentication prompt in the next * before it is able to finish. This will cause the browser to show a new authentication prompt in the next
* request. * request.
*/ */
window.onload = function () { $(document).ready(function() {
function getXMLHttpRequest() { msg = $('#logout-status');
var xmlhttp = null;
try {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
} catch (e) {}
return xmlhttp;
}
var msg = document.getElementById('logout-status');
try { try {
if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) { if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) {
document.execCommand('ClearAuthenticationCache'); document.execCommand('ClearAuthenticationCache');
@ -60,13 +39,9 @@
xhttp.abort(); xhttp.abort();
} }
} catch (e) { } catch (e) {
msg.innerHTML = '<?= t(
'Logout not possible, it may be necessary to quit the session manually ' .
'by clearing the cache, or closing the current browser session. Error: '
);?>' + ': ' + e.getMessage() ;
msg.setAttribute('class', 'alert alert-danger');
} }
msg.innerHTML = '<?= t('Logout successful!'); ?>'; msg.html('<?= t('Logout successful!'); ?>');
msg.setAttribute('class', 'alert alert-success'); msg.removeClass();
}; msg.addClass('alert alert-success');
});
</script> </script>

View File

@ -18,10 +18,15 @@ title = "Configuration"
url = "config" url = "config"
priority = 300 priority = 300
[System.Modules]
title = "Modules"
url = "config/modules"
priority = 400
[System.ApplicationLog] [System.ApplicationLog]
title = "Application log" title = "Application log"
url = "list/applicationlog" url = "list/applicationlog"
priority = 400 priority = 500
[Logout] [Logout]
url = "authentication/logout" url = "authentication/logout"

View File

@ -3,10 +3,19 @@ Alias @web_path@ "@prefix@/public"
<Directory "@prefix@/public"> <Directory "@prefix@/public">
Options SymLinksIfOwnerMatch Options SymLinksIfOwnerMatch
AllowOverride None AllowOverride None
Order allow,deny
Allow from all <IfModule mod_authz_core.c>
# new directive needed in Apache 2.4.3+ # Apache 2.4
#Require all granted <RequireAll>
Require all granted
</RequireAll>
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order allow,deny
Allow from all
</IfModule>
SetEnv ICINGAWEB_CONFIGDIR @icingaweb_config_path@ SetEnv ICINGAWEB_CONFIGDIR @icingaweb_config_path@

View File

@ -1,16 +1,37 @@
# $Id$ #/**
# Authority: The icinga devel team <icinga-devel at lists.icinga.org> # * This file is part of Icinga Web 2.
# Upstream: The icinga devel team <icinga-devel at lists.icinga.org> # *
# ExcludeDist: el4 el3 # * Icinga Web 2 - Head for multiple monitoring backends.
# * Copyright (C) 2014 Icinga Development Team
# *
# * This program is free software; you can redistribute it and/or
# * modify it under the terms of the GNU General Public License
# * as published by the Free Software Foundation; either version 2
# * of the License, or (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# *
# * @copyright 2014 Icinga Development Team <info@icinga.org>
# * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
# * @author Icinga Development Team <info@icinga.org>
# *
# */
%define revision 0 %define revision 1
%define configdir %{_sysconfdir}/icingaweb %define configdir %{_sysconfdir}/icingaweb
%define sharedir %{_datadir}/icingaweb %define sharedir %{_datadir}/icingaweb
%define prefixdir %{_datadir}/icingaweb %define prefixdir %{_datadir}/icingaweb
%define logdir %{sharedir}/log %define logdir %{sharedir}/log
%define usermodparam -a -G %define usermodparam -a -G
#%define logdir %{_localstatedir}/log/icingaweb %define logdir %{_localstatedir}/log/icingaweb
%if "%{_vendor}" == "suse" %if "%{_vendor}" == "suse"
%define phpname php5 %define phpname php5
@ -19,11 +40,12 @@
%endif %endif
# SLE 11 = 1110 # SLE 11 = 1110
%if 0%{?suse_version} == 1110 %if 0%{?suse_version} == 1110
%define phpname php53
%define apache2modphpname apache2-mod_php53 %define apache2modphpname apache2-mod_php53
%define usermodparam -A %define usermodparam -A
%endif %endif
%if "%{_vendor}" == "redhat" || 0%{?suse_version} == 1110 %if "%{_vendor}" == "redhat"
%define phpname php %define phpname php
%define phpzendname php-ZendFramework %define phpzendname php-ZendFramework
%endif %endif
@ -37,15 +59,15 @@
%define apacheconfdir %{_sysconfdir}/apache2/conf.d %define apacheconfdir %{_sysconfdir}/apache2/conf.d
%define apacheuser wwwrun %define apacheuser wwwrun
%define apachegroup www %define apachegroup www
%define extcmdfile1x %{_localstatedir}/icinga/rw/icinga.cmd %define extcmdfile %{_localstatedir}/run/icinga2/cmd/icinga.cmd
%define livestatussocket1x %{_localstatedir}/icinga/rw/live %define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus
%endif %endif
%if "%{_vendor}" == "redhat" %if "%{_vendor}" == "redhat"
%define apacheconfdir %{_sysconfdir}/httpd/conf.d %define apacheconfdir %{_sysconfdir}/httpd/conf.d
%define apacheuser apache %define apacheuser apache
%define apachegroup apache %define apachegroup apache
%define extcmdfile-1x %{_localstatedir}/spool/icinga/cmd/icinga.cmd %define extcmdfile %{_localstatedir}/run/icinga2/cmd/icinga.cmd
%define livestatussocket1x %{_localstatedir}/spool/icinga/cmd/live %define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus
%endif %endif
Summary: Open Source host, service and network monitoring Web UI Summary: Open Source host, service and network monitoring Web UI
@ -109,8 +131,8 @@ Requires: php-Icinga
%description %description
IcingaWeb for Icinga 2 or Icinga 1.x using status data, Icinga Web 2 for Icinga 2 or Icinga 1.x using multiple backends
IDOUtils or Livestatus as backend provider. for example DB IDO.
%package -n icingacli %package -n icingacli
Summary: Icinga CLI Summary: Icinga CLI
@ -130,58 +152,54 @@ Requires: %{phpzendname}
%description -n php-Icinga %description -n php-Icinga
Icinga Web 2 PHP Libraries shared with icingacli. Icinga Web 2 PHP Libraries required by the web frontend and cli tool.
%prep %prep
#%setup -q -n %{name}-%{version} #VERSION=0.0.1; git archive --format=tar --prefix=icingaweb2-$VERSION/ HEAD | gzip >icingaweb2-$VERSION.tar.gz
%setup -q -n %{name} %setup -q -n %{name}-%{version}
%build %build
cat > README.RHEL.SUSE <<"EOF"
IcingaWeb for RHEL and SUSE
===========================
Please check ./doc/installation.md
for requirements and database setup.
EOF
%install %install
[ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot} [ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot}
# prepare configuration for sub packages # prepare configuration for sub packages
# install rhel apache config # install rhel apache config
install -D -m0644 packages/rhel/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf install -D -m0644 packages/rpm/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf
# install public, library, modules # install public, library, modules
%{__mkdir} -p %{buildroot}/%{sharedir} %{__mkdir} -p %{buildroot}/%{sharedir}
%{__mkdir} -p %{buildroot}/%{logdir} %{__mkdir} -p %{buildroot}/%{logdir}
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/dashboard
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules %{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules
%{__cp} -r application library modules public %{buildroot}/%{sharedir}/ %{__cp} -r application library modules public %{buildroot}/%{sharedir}/
# install index.php ## config
install -m0644 packages/rhel/usr/share/icingaweb/public/index.php %{buildroot}/%{sharedir}/public/index.php # use the default menu.ini for application and monitoring mobule
install -D -m0644 config/menu.ini %{buildroot}/%{_sysconfdir}/icingaweb/menu.ini
# use the vagrant config for configuration for now - TODO install -D -m0644 config/modules/monitoring/menu.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/menu.ini
%{__cp} -r .vagrant-puppet/files/etc/icingaweb %{buildroot}/%{_sysconfdir}/ # authentication is db only
install -D -m0644 packages/rpm/etc/icingaweb/authentication.ini %{buildroot}/%{_sysconfdir}/icingaweb/authentication.ini
# we use the default 'icinga' database # custom resource paths
sed -i 's/icinga2/icinga/g' %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini install -D -m0644 packages/rpm/etc/icingaweb/resources.ini %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini
# dashboard
install -D -m0644 config/dashboard/dashboard.ini %{buildroot}/%{_sysconfdir}/icingaweb/dashboard/dashboard.ini
# monitoring module (icinga2)
install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/backends.ini
install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/instances.ini
# enable the monitoring module by default # enable the monitoring module by default
ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring
## config
# install icingacli # install icingacli
install -D -m0755 bin/icingacli %{buildroot}/usr/bin/icingacli install -D -m0755 packages/rpm/usr/bin/icingacli %{buildroot}/usr/bin/icingacli
# install sql schema files as example
# delete all *.in files
%pre %pre
# Add apacheuser in the icingacmd group # Add apacheuser in the icingacmd group
@ -196,9 +214,6 @@ if [ $? -eq 0 ]; then
%{_sbindir}/usermod %{usermodparam} icingacmd %{apacheuser} %{_sbindir}/usermod %{usermodparam} icingacmd %{apacheuser}
fi fi
# uncomment if building from git
# %{__rm} -rf %{buildroot}%{_datadir}/icinga2-web/.git
%preun %preun
%post %post
@ -209,14 +224,13 @@ fi
%files %files
# main dirs # main dirs
%defattr(-,root,root) %defattr(-,root,root)
%doc etc/schema doc packages/rhel/README %doc etc/schema doc packages/rpm/README.md
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/public %attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/public
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/modules %attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/modules
# configs # configs
%defattr(-,root,root) %defattr(-,root,root)
%config(noreplace) %attr(-,root,root) %{apacheconfdir}/icingaweb.conf %config(noreplace) %attr(-,root,root) %{apacheconfdir}/icingaweb.conf
%dir %{configdir} %config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir}
%config(noreplace) %attr(775,%{apacheuser},%{apachegroup}) %{configdir}
# logs # logs
%attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir} %attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir}
@ -228,6 +242,3 @@ fi
%attr(0755,root,root) /usr/bin/icingacli %attr(0755,root,root) /usr/bin/icingacli
%changelog %changelog
* Tue May 11 2014 Michael Friedrich <michael.friedrich@netways.de> - 0.0.1-1
- initial creation

View File

@ -53,6 +53,7 @@ class AutoLoginBackend extends UserBackend
{ {
if (isset($_SERVER['REMOTE_USER'])) { if (isset($_SERVER['REMOTE_USER'])) {
$username = $_SERVER['REMOTE_USER']; $username = $_SERVER['REMOTE_USER'];
$user->setRemoteUserInformation($username, 'REMOTE_USER');
if ($this->stripUsernameRegexp !== null) { if ($this->stripUsernameRegexp !== null) {
$stripped = preg_replace($this->stripUsernameRegexp, '', $username); $stripped = preg_replace($this->stripUsernameRegexp, '', $username);
if ($stripped !== false) { if ($stripped !== false) {

View File

@ -30,12 +30,6 @@ class Manager
*/ */
private $user; private $user;
/**
* If the user was authenticated from the REMOTE_USER server variable
*
* @var Boolean
*/
private $fromRemoteUser = false;
private function __construct() private function __construct()
{ {
@ -117,6 +111,13 @@ class Manager
public function authenticateFromSession() public function authenticateFromSession()
{ {
$this->user = Session::getSession()->get('user'); $this->user = Session::getSession()->get('user');
if ($this->user !== null && $this->user->isRemoteUser() === true) {
list($originUsername, $field) = $this->user->getRemoteUserInformation();
if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization();
}
}
} }
/** /**
@ -204,35 +205,4 @@ class Manager
{ {
return $this->user->getGroups(); return $this->user->getGroups();
} }
/**
* Tries to authenticate the user from the session, and then from the REMOTE_USER superglobal, that can be set by
* an external authentication provider.
*/
public function authenticateFromRemoteUser()
{
if (array_key_exists('REMOTE_USER', $_SERVER)) {
$this->fromRemoteUser = true;
}
$this->authenticateFromSession();
if ($this->user !== null) {
if (array_key_exists('REMOTE_USER', $_SERVER) && $this->user->getUsername() !== $_SERVER["REMOTE_USER"]) {
// Remote user has changed, clear all sessions
$this->removeAuthorization();
}
return;
}
if (array_key_exists('REMOTE_USER', $_SERVER) && $_SERVER["REMOTE_USER"]) {
$this->user = new User($_SERVER["REMOTE_USER"]);
$this->persistCurrentUser();
}
}
/**
* If the session was established from the REMOTE_USER server variable.
*/
public function isAuthenticatedFromRemoteUser()
{
return $this->fromRemoteUser;
}
} }

View File

@ -7,6 +7,7 @@ namespace Icinga\Cli;
use Icinga\Cli\Screen; use Icinga\Cli\Screen;
use Icinga\Util\Translator; use Icinga\Util\Translator;
use Icinga\Cli\Params; use Icinga\Cli\Params;
use Icinga\Application\Config;
use Icinga\Application\ApplicationBootstrap as App; use Icinga\Application\ApplicationBootstrap as App;
use Exception; use Exception;
@ -23,6 +24,10 @@ abstract class Command
protected $commandName; protected $commandName;
protected $actionName; protected $actionName;
private $config;
private $configs;
protected $defaultActionName = 'default'; protected $defaultActionName = 'default';
public function __construct(App $app, $moduleName, $commandName, $actionName, $initialize = true) public function __construct(App $app, $moduleName, $commandName, $actionName, $initialize = true)
@ -41,6 +46,51 @@ abstract class Command
} }
} }
public function Config($file = null)
{
if ($this->isModule()) {
return $this->getModuleConfig($file);
} else {
return $this->getMainConfig($file);
}
}
private function getModuleConfig($file = null)
{
if ($file === null) {
if ($this->config === null) {
$this->config = Config::module($this->moduleName);
}
return $this->config;
} else {
if (! array_key_exists($file, $this->configs)) {
$this->configs[$file] = Config::module($this->moduleName, $file);
}
return $this->configs[$file];
}
}
private function getMainConfig($file = null)
{
if ($file === null) {
if ($this->config === null) {
$this->config = Config::app();
}
return $this->config;
} else {
if (! array_key_exists($file, $this->configs)) {
$this->configs[$file] = Config::module($module, $file);
}
return $this->configs[$file];
}
return $this->config;
}
public function isModule()
{
return substr(get_class($this), 0, 14) === 'Icinga\\Module\\';
}
public function setParams(Params $params) public function setParams(Params $params)
{ {
$this->params = $params; $this->params = $params;

View File

@ -109,7 +109,19 @@ class CommandPipe
*/ */
public function send($command) public function send($command)
{ {
$this->transport->send($command); $this->transport->send($this->escape($command));
}
/**
* Return the given command string with escaped newlines
*
* @param string $command The command string to escape
*
* @return string The escaped command string
*/
public function escape($command)
{
return str_replace(array("\r", "\n"), array('\r', '\n'), $command);
} }
/** /**
@ -121,16 +133,14 @@ class CommandPipe
public function sendCommand(Command $command, array $objects = array()) public function sendCommand(Command $command, array $objects = array())
{ {
if ($command->provideGlobalCommand() === true) { if ($command->provideGlobalCommand() === true) {
$this->transport->send($command->getGlobalCommand()); $this->send($command->getGlobalCommand());
} else { } else {
foreach ($objects as $object) { foreach ($objects as $object) {
$objectType = $this->getObjectType($object); $objectType = $this->getObjectType($object);
if ($objectType === self::TYPE_SERVICE) { if ($objectType === self::TYPE_SERVICE) {
$this->transport->send( $this->send($command->getServiceCommand($object->host_name, $object->service_description));
$command->getServiceCommand($object->host_name, $object->service_description)
);
} else { } else {
$this->transport->send($command->getHostCommand($object->host_name)); $this->send($command->getHostCommand($object->host_name));
} }
} }
} }

View File

@ -58,6 +58,18 @@ class User
*/ */
protected $additionalInformation = array(); protected $additionalInformation = array();
/**
* Information if the user is external authenticated
*
* Keys:
*
* 0: origin username
* 1: origin field name
*
* @var array
*/
protected $remoteUserInformation = array();
/** /**
* Set of permissions * Set of permissions
* *
@ -401,4 +413,35 @@ class User
{ {
$this->messages = null; $this->messages = null;
} }
/**
* Set additional remote user information
*
* @param stirng $username
* @param string $field
*/
public function setRemoteUserInformation($username, $field)
{
$this->remoteUserInformation = array($username, $field);
}
/**
* Get additional remote user information
*
* @return array
*/
public function getRemoteUserInformation()
{
return $this->remoteUserInformation;
}
/**
* Return true if user has remote user information set
*
* @return bool
*/
public function isRemoteUser()
{
return (count($this->remoteUserInformation)) ? true : false;
}
} }

View File

@ -115,7 +115,7 @@ class Translator
*/ */
public static function getAvailableLocaleCodes() public static function getAvailableLocaleCodes()
{ {
$codes = array(); $codes = array(static::DEFAULT_LOCALE);
foreach (array_values(self::$knownDomains) as $directory) { foreach (array_values(self::$knownDomains) as $directory) {
$dh = opendir($directory); $dh = opendir($directory);
while (false !== ($name = readdir($dh))) { while (false !== ($name = readdir($dh))) {

View File

@ -254,10 +254,27 @@ class ActionController extends Zend_Controller_Action
* *
* @throws \Exception * @throws \Exception
*/ */
protected function redirectToLogin($afterLogin = '/dashboard') protected function redirectToLogin($afterLogin = null)
{ {
$redir = null;
if ($afterLogin !== null) {
if (! $afterLogin instanceof Url) {
$afterLogin = Url::fromPath($afterLogin);
}
if ($this->isXhr()) {
$redir = '__SELF__';
} else {
// TODO: Ignore /?
$redir = $afterLogin->getRelativeUrl();
}
}
$url = Url::fromPath('authentication/login'); $url = Url::fromPath('authentication/login');
$url->setParam('redirect', $afterLogin);
if ($redir) {
$url->setParam('redirect', $redir);
}
$this->rerenderLayout()->redirectNow($url); $this->rerenderLayout()->redirectNow($url);
} }
@ -273,6 +290,27 @@ class ActionController extends Zend_Controller_Action
return $this->getRequest()->isXmlHttpRequest(); return $this->getRequest()->isXmlHttpRequest();
} }
protected function redirectXhr($url)
{
if (! $url instanceof Url) {
$url = Url::fromPath($url);
}
if ($this->rerenderLayout) {
$this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes');
}
if ($this->reloadCss) {
$this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now');
}
$this->getResponse()
->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl()))
->sendHeaders();
// TODO: Session shutdown?
exit;
}
/** /**
* Redirect to a specific url, updating the browsers URL field * Redirect to a specific url, updating the browsers URL field
* *
@ -280,26 +318,13 @@ class ActionController extends Zend_Controller_Action
**/ **/
public function redirectNow($url) public function redirectNow($url)
{ {
if (! $url instanceof Url) {
$url = Url::fromPath($url);
}
$url = preg_replace('~&amp;~', '&', $url);
if ($this->isXhr()) { if ($this->isXhr()) {
if ($this->rerenderLayout) { $this->redirectXhr($url);
$this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes');
}
if ($this->reloadCss) {
$this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now');
}
$this->getResponse()
->setHeader('X-Icinga-Redirect', rawurlencode($url))
->sendHeaders();
// TODO: Session shutdown?
exit;
} else { } else {
$this->_helper->Redirector->gotoUrlAndExit(Url::fromPath($url)->getRelativeUrl()); if (! $url instanceof Url) {
$url = Url::fromPath($url);
}
$this->_helper->Redirector->gotoUrlAndExit($url->getRelativeUrl());
} }
} }
@ -362,6 +387,8 @@ class ActionController extends Zend_Controller_Action
'X-Icinga-Title', 'X-Icinga-Title',
rawurlencode($this->view->title . ' :: Icinga Web') rawurlencode($this->view->title . ' :: Icinga Web')
); );
} else {
$resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web'));
} }
if ($this->rerenderLayout) { if ($this->rerenderLayout) {

View File

@ -127,10 +127,6 @@ class Url
$baseUrl = $request->getBaseUrl(); $baseUrl = $request->getBaseUrl();
$urlObject->setBaseUrl($baseUrl); $urlObject->setBaseUrl($baseUrl);
// Fetch fragment manually and remove it from the url, to 'help' the parse_url() function
// parsing the url properly. Otherwise calling the function with a fragment, but without a
// query will cause unpredictable behaviour.
$url = self::stripUrlFragment($url);
$urlParts = parse_url($url); $urlParts = parse_url($url);
if (isset($urlParts['path'])) { if (isset($urlParts['path'])) {
if ($baseUrl !== '' && strpos($urlParts['path'], $baseUrl) === 0) { if ($baseUrl !== '' && strpos($urlParts['path'], $baseUrl) === 0) {
@ -144,44 +140,14 @@ class Url
$params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params); $params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params);
} }
$fragment = self::getUrlFragment($url); if (isset($urlParts['fragment'])) {
if ($fragment !== '') { $urlObject->setAnchor($urlParts['fragment']);
$urlObject->setAnchor($fragment);
} }
$urlObject->setParams($params); $urlObject->setParams($params);
return $urlObject; return $urlObject;
} }
/**
* Get the fragment of a given url
*
* @param string $url The url containing the fragment.
*
* @return string The fragment without the '#'
*/
protected static function getUrlFragment($url)
{
$url = parse_url($url);
if (isset($url['fragment'])) {
return $url['fragment'];
} else {
return '';
}
}
/**
* Remove the fragment-part of a given url
*
* @param string $url The url to strip from its fragment
*
* @return string The url without the fragment
*/
protected static function stripUrlFragment($url)
{
return preg_replace('/#.*$/', '', $url);
}
/** /**
* Overwrite the baseUrl * Overwrite the baseUrl
* *
@ -241,12 +207,12 @@ class Url
* *
* @return string * @return string
*/ */
public function getRelativeUrl() public function getRelativeUrl($separator = '&')
{ {
if ($this->params->isEmpty()) { if ($this->params->isEmpty()) {
return $this->path . $this->anchor; return $this->path . $this->anchor;
} else { } else {
return $this->path . '?' . $this->params->setSeparator('&amp;') . $this->anchor; return $this->path . '?' . $this->params->toString($separator) . $this->anchor;
} }
} }
@ -266,9 +232,9 @@ class Url
* *
* @return string * @return string
*/ */
public function getAbsoluteUrl() public function getAbsoluteUrl($separator = '&')
{ {
return $this->baseUrl . ($this->baseUrl !== '/' ? '/' : '') . $this->getRelativeUrl(); return $this->baseUrl . ($this->baseUrl !== '/' ? '/' : '') . $this->getRelativeUrl($separator);
} }
/** /**
@ -450,6 +416,6 @@ class Url
*/ */
public function __toString() public function __toString()
{ {
return $this->getAbsoluteUrl(); return $this->getAbsoluteUrl('&amp;');
} }
} }

View File

@ -114,6 +114,18 @@ class UrlParams
return $ret; return $ret;
} }
public function addEncoded($param, $value = true)
{
$this->params[] = array($param, $this->cleanupValue($value));
$this->indexLastOne();
return $this;
}
protected function urlEncode($value)
{
return rawurlencode((string) $value);
}
/** /**
* Add the given parameter with the given value * Add the given parameter with the given value
* *
@ -127,9 +139,7 @@ class UrlParams
*/ */
public function add($param, $value = true) public function add($param, $value = true)
{ {
$this->params[] = array($param, $this->cleanupValue($value)); return $this->addEncoded($this->urlEncode($param), $this->urlEncode($value));
$this->indexLastOne();
return $this;
} }
/** /**
@ -198,7 +208,7 @@ class UrlParams
*/ */
public function unshift($param, $value) public function unshift($param, $value)
{ {
array_unshift($this->params, array($param, $this->cleanupValue($value))); array_unshift($this->params, array($this->urlEncode($param), $this->urlEncode($value)));
$this->reIndexAll(); $this->reIndexAll();
return $this; return $this;
} }
@ -224,7 +234,10 @@ class UrlParams
unset($this->params[$remove]); unset($this->params[$remove]);
} }
$this->params[$this->index[$param][0]] = array($param, $this->cleanupValue($value)); $this->params[$this->index[$param][0]] = array(
$this->urlEncode($param),
$this->urlEncode($this->cleanupValue($value))
);
$this->reIndexAll(); $this->reIndexAll();
return $this; return $this;
@ -243,7 +256,7 @@ class UrlParams
foreach ($this->index[$p] as $key) { foreach ($this->index[$p] as $key) {
unset($this->params[$key]); unset($this->params[$key]);
} }
$this->changed = true; $changed = true;
} }
} }
@ -303,10 +316,10 @@ class UrlParams
protected function parseQueryStringPart($part) protected function parseQueryStringPart($part)
{ {
if (strpos($part, '=') === false) { if (strpos($part, '=') === false) {
$this->add($part, true); $this->addEncoded($part, true);
} else { } else {
list($key, $val) = preg_split('/=/', $part, 2); list($key, $val) = preg_split('/=/', $part, 2);
$this->add($key, $val); $this->addEncoded($key, $val);
} }
} }
@ -315,8 +328,11 @@ class UrlParams
return $this->params; return $this->params;
} }
public function __toString() public function toString($separator = null)
{ {
if ($separator === null) {
$separator = $this->separator;
}
$parts = array(); $parts = array();
foreach ($this->params as $p) { foreach ($this->params as $p) {
if ($p[1] === true) { if ($p[1] === true) {
@ -325,7 +341,12 @@ class UrlParams
$parts[] = $p[0] . '=' . $p[1]; $parts[] = $p[0] . '=' . $p[1];
} }
} }
return implode($this->separator, $parts); return implode($separator, $parts);
}
public function __toString()
{
return $this->toString();
} }
public static function fromQueryString($queryString = null) public static function fromQueryString($queryString = null)

View File

@ -28,7 +28,7 @@ class DashboardAction implements Tabextension
'title' => 'Add To Dashboard', 'title' => 'Add To Dashboard',
'url' => Url::fromPath('dashboard/addurl'), 'url' => Url::fromPath('dashboard/addurl'),
'urlParams' => array( 'urlParams' => array(
'url' => Url::fromRequest()->getRelativeUrl() 'url' => rawurlencode(Url::fromRequest()->getRelativeUrl())
) )
) )
); );

View File

@ -72,6 +72,7 @@ class ListCommand extends Command
protected function showFormatted($query, $format, $columns) protected function showFormatted($query, $format, $columns)
{ {
$query = $query->getQuery();
switch($format) { switch($format) {
case 'json': case 'json':
echo json_encode($query->fetchAll()); echo json_encode($query->fetchAll());
@ -155,7 +156,7 @@ class ListCommand extends Command
'service_perfdata', 'service_perfdata',
'service_last_state_change' 'service_last_state_change'
); );
$query = $this->getQuery('status', $columns) $query = $this->getQuery('serviceStatus', $columns)
->order('host_name'); ->order('host_name');
echo $this->renderStatusQuery($query); echo $this->renderStatusQuery($query);
} }
@ -167,6 +168,7 @@ class ListCommand extends Command
$screen = $this->screen; $screen = $this->screen;
$utils = new CliUtils($screen); $utils = new CliUtils($screen);
$maxCols = $screen->getColumns(); $maxCols = $screen->getColumns();
$query = $query->getQuery();
$rows = $query->fetchAll(); $rows = $query->fetchAll();
$count = $query->count(); $count = $query->count();
$count = count($rows); $count = count($rows);

View File

@ -10,28 +10,33 @@ $cf = $this->getHelper('CommandForm');
<div class="content processinfo"> <div class="content processinfo">
<p>Backend <strong><?= $this->backendName; ?></strong> <p>Backend <strong><?= $this->backendName; ?></strong>
<?= $ps->is_currently_running === '1' ? sprintf('has been running with PID %d ', $ps->process_id) . $this->prefixedTimeSince($ps->program_start_time) : 'is not running'; ?>. <?= $ps->is_currently_running === '1'
? sprintf(
$this->translate('has been running with PID %d '),
$ps->process_id
) . $this->prefixedTimeSince($ps->program_start_time)
: $this->translate('is not running'); ?>.
<table class="avp"> <table class="avp">
<tbody> <tbody>
<tr> <tr>
<th>Last status update</th> <th><?= $this->translate('Last status update'); ?></th>
<td><?= $this->timeSince($ps->status_update_time) ?> ago</td> <td><?= $this->timeSince($ps->status_update_time) ?> ago</td>
</tr> </tr>
<tr> <tr>
<th>Last check command</th> <th><?= $this->translate('Last check command'); ?></th>
<td><?= $this->timeSince($ps->last_command_check) ?> ago</td> <td><?= $this->timeSince($ps->last_command_check) ?> ago</td>
</tr> </tr>
<tr> <tr>
<th>Global host event handler</th> <th><?= $this->translate('Global host event handler'); ?></th>
<td><?= $ps->global_host_event_handler ? $ps->global_host_event_handler : 'Not set' ?></td> <td><?= $ps->global_host_event_handler ? $ps->global_host_event_handler : $this->translate('Not set'); ?></td>
</tr> </tr>
<tr> <tr>
<th>Global service event handler</th> <th><?= $this->translate('Global service event handler'); ?></th>
<td><?= $ps->global_service_event_handler ? $ps->global_service_event_handler : 'Not set' ?></td> <td><?= $ps->global_service_event_handler ? $ps->global_service_event_handler : $this->translate('Not set'); ?></td>
</tr> </tr>
<tr> <tr>
<th>Notifications enabled</th> <th><?= $this->translate('Notifications enabled'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->notifications_enabled, $ps->notifications_enabled,
@ -41,15 +46,18 @@ $cf = $this->getHelper('CommandForm');
array('global' => '1') array('global' => '1')
) ?> ) ?>
<?php if ($ps->notifications_enabled === '1'): ?> <?php if ($ps->notifications_enabled === '1'): ?>
<a rel="tooltip" title="Disable notifications for a specific time on a program-wide basis" href="<?= $this->href('monitoring/command/disablenotificationswithexpire') ?>" data-base-target="_next">Temporarily disable</a> <a rel="tooltip" title="<?= $this->translate('Disable notifications for a specific time on a program-wide basis'); ?>"
href="<?= $this->href('monitoring/command/disablenotificationswithexpire') ?>" data-base-target="_next">
<?= $this->translate('Temporarily disable'); ?>
</a>
<?php elseif ($ps->disable_notif_expire_time): ?> <?php elseif ($ps->disable_notif_expire_time): ?>
Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_time) ?></strong> <?= sprintf($this->translate('Will be re-enabled in %s'), '<strong>' . $this->timeUntil($ps->disable_notif_expire_time) . '</strong>'); ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
<tr> <tr>
<th>Execute active service checks</th> <th><?= $this->translate('Execute active service checks'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->active_service_checks_enabled, $ps->active_service_checks_enabled,
@ -60,7 +68,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Accept passive service checks</th> <th><?= $this->translate('Accept passive service checks'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->passive_service_checks_enabled, $ps->passive_service_checks_enabled,
@ -71,7 +79,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Execute active host checks</th> <th><?= $this->translate('Execute active host checks'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->active_host_checks_enabled, $ps->active_host_checks_enabled,
@ -82,7 +90,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Accept passive host checks</th> <th><?= $this->translate('Accept passive host checks'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->passive_host_checks_enabled, $ps->passive_host_checks_enabled,
@ -93,7 +101,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Eventhandlers enabled</th> <th><?= $this->translate('Eventhandlers enabled'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->event_handlers_enabled, $ps->event_handlers_enabled,
@ -104,7 +112,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Obsessing over host checks</th> <th><?= $this->translate('Obsessing over host checks'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->obsess_over_hosts, $ps->obsess_over_hosts,
@ -115,7 +123,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Obsessing over service checks</th> <th><?= $this->translate('Obsessing over service checks'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->obsess_over_services, $ps->obsess_over_services,
@ -126,7 +134,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Flap detection enabled</th> <th><?= $this->translate('Flap detection enabled'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->flap_detection_enabled, $ps->flap_detection_enabled,
@ -137,7 +145,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Process performance data</th> <th><?= $this->translate('Process performance data'); ?></th>
<td><?= $cf->toggleSubmitForm( <td><?= $cf->toggleSubmitForm(
'', '',
$ps->process_performance_data, $ps->process_performance_data,
@ -148,17 +156,17 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
) ?></td> ) ?></td>
</tr> </tr>
<tr> <tr>
<th>Monitoring Process</th> <th><?= $this->translate('Monitoring Process'); ?></th>
<td><?= $cf->labelSubmitForm( <td><?= $cf->labelSubmitForm(
'Restart', $this->translate('Restart'),
'Restart the monitoring process', $this->translate('Restart the monitoring process'),
'', '',
'restartprocess' 'restartprocess'
) ?> ) ?>
<!--<?= <!--<?=
$cf->labelSubmitForm( $cf->labelSubmitForm(
'Shutdown monitoring process', $this->translate('Shutdown monitoring process'),
'Shutdown the monitoring process', $this->translate('Shutdown the monitoring process'),
'btn-cta', 'btn-cta',
'shutdownprocess' 'shutdownprocess'
); );

View File

@ -31,12 +31,12 @@ foreach ($object->comments as $comment) {
); );
$list[] = sprintf( $list[] = sprintf(
'<tr><th>%s (%s)</th><td data-base-target="_self">%s (%s) %s</td></tr>', '<tr><th>%s (%s)</th><td><table><tr><td style="vertical-align: top;" data-base-target="_self">%s (%s):</td><td style="padding-left: .5em;">%s</td></tr></table></td></tr>',
$this->escape($comment->author), $this->escape($comment->author),
$this->timeSince($comment->timestamp), $this->timeSince($comment->timestamp),
$iconForm, $iconForm,
ucfirst($comment->type), ucfirst($comment->type),
$text str_replace(array('\r\n', '\n'), '<br>', $text)
); );
} }

View File

@ -42,11 +42,11 @@ foreach ($object->downtimes as $downtime) {
) : $this->escape($downtime->comment); ) : $this->escape($downtime->comment);
$list[] = sprintf( $list[] = sprintf(
'<tr><th>%s</th><td data-base-target="_self">%s %s: %s</td></tr>', '<tr><th>%s</th><td><table><tr><td style="vertical-align: top;" data-base-target="_self">%s %s:</td><td style="padding-left: .5em;">%s</td></tr></table></td></tr>',
$this->escape($downtime->author), $this->escape($downtime->author),
$iconForm, $iconForm,
$state, $state,
$text str_replace(array('\r\n', '\n'), '<br>', $text)
); );
} }

View File

@ -48,25 +48,25 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g
<?php if ($firstRow && $extrapolatedCircleWidth !== $circleWidth): ?> <?php if ($firstRow && $extrapolatedCircleWidth !== $circleWidth): ?>
<div class="circle-box" style="width: <?= $extrapolatedCircleWidth; ?>;"> <div class="circle-box" style="width: <?= $extrapolatedCircleWidth; ?>;">
<div class="outer-circle extrapolated" style="<?= sprintf( <div class="outer-circle extrapolated" style="<?= sprintf(
'width: %4$s; height: %4$s; border-color: %3$s; background-color: %1$s; margin-top: -%2$s;', 'width: %4$s; height: %4$s; border-color: %3$s; background-color: %1$s; margin-top: -%2$Fem;',
Color::changeBrightness($timeInfo[1][$groupName]->getColor(), 0.7), Color::changeBrightness($timeInfo[1][$groupName]->getColor(), 0.7),
((float) substr($extrapolatedCircleWidth, 0, -2) / 2) . 'em', (float) substr($extrapolatedCircleWidth, 0, -2) / 2,
$timeInfo[1][$groupName]->getColor(), $timeInfo[1][$groupName]->getColor(),
$extrapolatedCircleWidth $extrapolatedCircleWidth
); ?>"> ); ?>">
<?php else: ?> <?php else: ?>
<div class="circle-box" style="width: <?= $circleWidth; ?>;"> <div class="circle-box" style="width: <?= $circleWidth; ?>;">
<div class="outer-circle" style="<?= sprintf( <div class="outer-circle" style="<?= sprintf(
'width: %2$s; height: %2$s; margin-top: -%1$s;', 'width: %2$s; height: %2$s; margin-top: -%1$Fem;',
((float) substr($circleWidth, 0, -2) / 2) . 'em', (float) substr($circleWidth, 0, -2) / 2,
$circleWidth $circleWidth
); ?>"> ); ?>">
<?php endif ?> <?php endif ?>
<a class="inner-circle" style="<?= sprintf( <a class="inner-circle" style="<?= sprintf(
'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$s; margin-left: -%1$s;', 'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;',
((float) substr($circleWidth, 0, -2) / 2) . 'em', (float) substr($circleWidth, 0, -2) / 2,
$timeInfo[1][$groupName]->getColor(), $timeInfo[1][$groupName]->getColor(),
$circleWidth (string) $circleWidth
); ?>" href="<?= $timeInfo[1][$groupName]->getDetailUrl()->overwriteParams( ); ?>" href="<?= $timeInfo[1][$groupName]->getDetailUrl()->overwriteParams(
array( array(
'timestamp<' => $timeInfo[0]->start->getTimestamp(), 'timestamp<' => $timeInfo[0]->start->getTimestamp(),

View File

@ -76,6 +76,11 @@ class HoststatusQuery extends IdoQuery
ELSE 4 ELSE 4
END END
END END
END
+
CASE WHEN hs.state_type = 1
THEN 8
ELSE 0
END' END'
), ),
'hostgroups' => array( 'hostgroups' => array(

View File

@ -130,6 +130,11 @@ class StatusQuery extends IdoQuery
ELSE 4 ELSE 4
END END
END END
END
+
CASE WHEN hs.state_type = 1
THEN 8
ELSE 0
END' END'
), ),
'hostgroups' => array( 'hostgroups' => array(
@ -266,6 +271,11 @@ class StatusQuery extends IdoQuery
END END
END END
END END
END
+
CASE WHEN ss.state_type = 1
THEN 8
ELSE 0
END' END'
), ),
'serviceproblemsummary' => array( 'serviceproblemsummary' => array(

View File

@ -190,6 +190,9 @@ class Perfdata
if ($this->maxValue !== null) { if ($this->maxValue !== null) {
$minValue = $this->minValue !== null ? $this->minValue : 0; $minValue = $this->minValue !== null ? $this->minValue : 0;
if ($this->maxValue - $minValue === 0.0) {
return null;
}
if ($this->value > $minValue) { if ($this->value > $minValue) {
return (($this->value - $minValue) / ($this->maxValue - $minValue)) * 100; return (($this->value - $minValue) / ($this->maxValue - $minValue)) * 100;
@ -267,9 +270,13 @@ class Perfdata
switch (count($parts)) switch (count($parts))
{ {
case 5: case 5:
$this->maxValue = self::convert($parts[4], $this->unit); if ($parts[4] !== '') {
$this->maxValue = self::convert($parts[4], $this->unit);
}
case 4: case 4:
$this->minValue = self::convert($parts[3], $this->unit); if ($parts[3] !== '') {
$this->minValue = self::convert($parts[3], $this->unit);
}
case 3: case 3:
// TODO(#6123): Tresholds have the same UOM and need to be converted as well! // TODO(#6123): Tresholds have the same UOM and need to be converted as well!
$this->criticalThreshold = trim($parts[2]) ? trim($parts[2]) : null; $this->criticalThreshold = trim($parts[2]) ? trim($parts[2]) : null;

View File

@ -347,6 +347,14 @@ class PerfdataTest extends BaseTestCase
Perfdata::fromString('test=25;;;50;100')->getPercentage(), Perfdata::fromString('test=25;;;50;100')->getPercentage(),
'Perfdata objects do return a percentage though their value is lower than it\'s allowed minimum' 'Perfdata objects do return a percentage though their value is lower than it\'s allowed minimum'
); );
$this->assertNull(
Perfdata::fromString('test=25;;;0;')->getPercentage(),
'Perfdata objects do not ignore empty max values when returning percentages'
);
$this->assertNull(
Perfdata::fromString('test=25;;;0;0')->getPercentage(),
'Perfdata objects do not ignore impossible min/max combinations when returning percentages'
);
} }
/** /**

View File

@ -1,57 +0,0 @@
Requirements
============
Disabled SELinux for sending commands via external command pipe
provided by Icinga (2) Core.
# setenforce 0
Webinterface Login
==================
The default credentials using the internal MySQL database are
icingaadmin:icinga
Internal MySQL DB Setup
=======================
# mysql -u root -p
CREATE USER `icingaweb`@`localhost` IDENTIFIED BY 'icingaweb';
CREATE DATABASE `icingaweb`;
GRANT ALL PRIVILEGES ON `icingaweb`.* TO `icingaweb`@`localhost`;
FLUSH PRIVILEGES;
quit
# mysql icingaweb < /usr/share/doc/icingaweb2-*/schema/accounts.mysql.sql
# mysql icingaweb < /usr/share/doc/icingaweb2-*/schema/preferences.mysql.sql
Modules
=======
The monitoring module is enabled by default.
Backend configuration
=====================
/etc/icingaweb/resources.ini
contains the database backend information. By default
the Icinga IDO DB is used by the monitoring module in
/etc/icingaweb/modules/monitoring/backends.ini
The external command pipe is required for sending commands
and configured for Icinga 2 in
/etc/icingaweb/modules/monitoring/instances.ini
Support
=======
Please use one of the listed support channels at https://support.icinga.org

View File

@ -1,12 +0,0 @@
Alias /icingaweb /usr/share/icingaweb/public
<Directory "/usr/share/icingaweb/public">
Options -Indexes
AllowOverride All
Order allow,deny
Allow from all
EnableSendfile Off
</Directory>

View File

@ -1,7 +0,0 @@
RewriteEngine on
RewriteBase /icingaweb
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

View File

@ -1,6 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
require_once dirname(__DIR__). '/library/Icinga/Application/webrouter.php';

89
packages/rpm/README.md Normal file
View File

@ -0,0 +1,89 @@
# Icinga Web 2 README for RPM Packages
This file will describe how to install Icinga Web 2 from an RPM
package (RHEL/CentOS/Fedora, SLES/OpenSUSE).
## Requirements
* EPEL/OBS Repository for Zend Framework
* Apache 2.2+
* PHP 5.3+, Zend Framework, PHP PDO MySQL/PostgreSQL, PHP LDAP (optional)
* MySQL or PostgreSQL for internal DB
* Icinga 1.x or 2.x providing an IDO database (default: `icinga`)
* Icinga 1.x or 2.x providing an external command pipe (default: `icinga2.cmd`)
### SELinux
Disabled SELinux for sending commands via external command pipe
provided by Icinga (2) Core.
setenforce 0
## Webinterface Login
The default credentials using the internal MySQL database are
`icingaadmin:icinga`
## Support
Please use one of the listed support channels at https://support.icinga.org
## Internal DB Setup
Decide whether to use MySQL or PostgreSQL.
### MySQL
mysql -u root -p
CREATE USER `icingaweb`@`localhost` IDENTIFIED BY 'icingaweb';
CREATE DATABASE `icingaweb`;
GRANT ALL PRIVILEGES ON `icingaweb`.* TO `icingaweb`@`localhost`;
FLUSH PRIVILEGES;
quit
mysql -u root -p icingaweb < /usr/share/doc/icingaweb2-*/schema/accounts.mysql.sql
mysql -u root -p icingaweb < /usr/share/doc/icingaweb2-*/schema/preferences.mysql.sql
### PostgreSQL
sudo su postgres
psql
postgres=# CREATE USER icingaweb WITH PASSWORD 'icingaweb';
postgres=# CREATE DATABASE icingaweb;
postgres=# \q
Add the `cingaweb` user for trusted authentication to your `pg_hba.conf` file
in `/var/lib/pgsql/data/pg_hba.conf` and restart the PostgreSQL server.
local icingaweb icingaweb trust
host icingaweb icingaweb 127.0.0.1/32 trust
host icingaweb icingaweb ::1/128 trust
Now install the `icingaweb` schema
bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2-*/schema/accounts.pgsql.sql
bash$ psql -U icingaweb -a -f /usr/share/doc/icingaweb2-*/schema/preferences.pgsql.sql
## Configuration
### Module Configuration
The monitoring module is enabled by default.
### Backend configuration
`/etc/icingaweb/resources.ini` contains the database backend information.
By default the Icinga 2 DB IDO is used by the monitoring module in
`/etc/icingaweb/modules/monitoring/backends.ini`
The external command pipe is required for sending commands
and configured for Icinga 2 in
`/etc/icingaweb/modules/monitoring/instances.ini`
### Authentication configuration
The `/etc/icingaweb/authentication.ini` file uses the internal database as
default. This requires the database being installed properly before
allowing users to login via web console.

View File

@ -0,0 +1,32 @@
Alias /icingaweb "/usr/share/icingaweb/public"
<Directory "/usr/share/icingaweb/public">
Options SymLinksIfOwnerMatch
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
<RequireAll>
Require all granted
</RequireAll>
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order allow,deny
Allow from all
</IfModule>
SetEnv ICINGAWEB_CONFIGDIR /etc/icingaweb
EnableSendfile Off
RewriteEngine on
RewriteBase /icingaweb/
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
</Directory>

View File

@ -0,0 +1,6 @@
[autologin]
backend = autologin
[internal_db_authentication]
backend = db
resource = internal_db

View File

@ -0,0 +1,8 @@
[localdb]
type = ido
resource = "ido"
[locallive]
disabled = "1"
type = livestatus
resource = livestatus

View File

@ -0,0 +1,2 @@
[icinga]
path = "/var/run/icinga2/cmd/icinga2.cmd"

View File

@ -0,0 +1,28 @@
[internal_db]
type = db
db = mysql
host = localhost
port = 3306
password = icingaweb
username = icingaweb
dbname = icingaweb
[ido]
type = db
db = mysql
host = localhost
port = 3306
password = icinga
username = icinga
dbname = icinga
[livestatus]
type = livestatus
socket = /var/run/icinga2/cmd/livestatus
[logfile]
type = file
filename = "/var/log/icingaweb/icingaweb.log"
fields = "/^(?<datetime>[0-9]{4}(-[0-9]{2}){2}T[0-9]{2}(:[0-9]{2}){2}(\\+[0-9]{2}:[0-9]{2})?) - (?<loglevel>[A-Za-z]+) - (?<message>.*)$/"
; format: PCRE
;

6
packages/rpm/usr/bin/icingacli Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/php
<?php
use Icinga\Application\Cli;
require_once '/usr/share/icingaweb/library/Icinga/Application/Cli.php';
Cli::start()->dispatch();

BIN
public/img/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

View File

@ -95,6 +95,13 @@ if (!Function.prototype.bind) {
'use strict'; 'use strict';
/* Whether a HTML tag has a specific attribute */
$.fn.hasAttr = function(name) {
// We have inconsistent behaviour across browsers (false VS undef)
var val = this.attr(name);
return typeof val !== 'undefined' && val !== false;
};
/* Get class list */ /* Get class list */
$.fn.classes = function (callback) { $.fn.classes = function (callback) {

View File

@ -355,12 +355,20 @@
$target = self.getLinkTargetFor($form); $target = self.getLinkTargetFor($form);
if (method === 'GET') { if (method === 'GET') {
url = icinga.utils.addUrlParams(url, $form.serializeObject()); var dataObj = $form.serializeObject();
if (typeof autosubmit === 'undefined' || ! autosubmit) {
if ($button.length && $button.attr('name') !== 'undefined') {
dataObj[$button.attr('name')] = $button.attr('value');
}
}
url = icinga.utils.addUrlParams(url, dataObj);
} else { } else {
data = $form.serializeArray(); data = $form.serializeArray();
if (typeof autosubmit === 'undefined' || ! autosubmit) { if (typeof autosubmit === 'undefined' || ! autosubmit) {
if ($button.length) { if ($button.length && $button.attr('name') !== 'undefined') {
data.push({ data.push({
name: $button.attr('name'), name: $button.attr('name'),
value: $button.attr('value') value: $button.attr('value')

View File

@ -162,11 +162,18 @@
parts = document.location.hash.split(/#!/); parts = document.location.hash.split(/#!/);
if ($('#col2').data('icingaUrl') !== main) { if ($('#layout > #login').length) {
icinga.loader.loadUrl( // We are on the login page!
parts[1], $('#login form #redirect').val(
$('#col2') $('#login form #redirect').val() + '#!' + parts[1]
).historyTriggered = true; );
} else {
if ($('#col2').data('icingaUrl') !== main) {
icinga.loader.loadUrl(
parts[1],
$('#col2')
).historyTriggered = true;
}
} }
// TODO: Replace with dynamic columns // TODO: Replace with dynamic columns

View File

@ -250,14 +250,24 @@
var icinga = this.icinga; var icinga = this.icinga;
var redirect = req.getResponseHeader('X-Icinga-Redirect'); var redirect = req.getResponseHeader('X-Icinga-Redirect');
if (! redirect) return false; if (! redirect) return false;
redirect = decodeURIComponent(redirect);
if (redirect.match(/__SELF__/)) {
redirect = redirect.replace(/__SELF__/, encodeURIComponent(document.location.pathname + document.location.search + document.location.hash));
}
icinga.logger.debug( icinga.logger.debug(
'Got redirect for ', req.$target, ', URL was ' + redirect 'Got redirect for ', req.$target, ', URL was ' + redirect
); );
redirect = decodeURIComponent(redirect);
if (req.getResponseHeader('X-Icinga-Rerender-Layout')) { if (req.getResponseHeader('X-Icinga-Rerender-Layout')) {
var parts = redirect.split(/#!/);
redirect = parts.shift();
var redirectionUrl = this.addUrlFlag(redirect, 'renderLayout'); var redirectionUrl = this.addUrlFlag(redirect, 'renderLayout');
this.loadUrl(redirectionUrl, $('#layout')).url = redirect; var r = this.loadUrl(redirectionUrl, $('#layout'));
r.url = redirect;
if (parts.length) {
r.loadNext = parts;
}
} else { } else {
if (req.$target.attr('id') === 'col2') { // TODO: multicol if (req.$target.attr('id') === 'col2') { // TODO: multicol
if ($('#col1').data('icingaUrl') === redirect) { if ($('#col1').data('icingaUrl') === redirect) {
@ -556,6 +566,17 @@
req.$target.data('lastUpdate', (new Date()).getTime()); req.$target.data('lastUpdate', (new Date()).getTime());
delete this.requests[req.$target.attr('id')]; delete this.requests[req.$target.attr('id')];
this.icinga.ui.fadeNotificationsAway(); this.icinga.ui.fadeNotificationsAway();
if (typeof req.loadNext !== 'undefined') {
if ($('#col2').length) {
this.loadUrl(req.loadNext[0], $('#col2'));
this.icinga.ui.layout2col();
} else {
this.icinga.logger.error('Failed to load URL for #col2', req.loadNext);
}
}
this.icinga.ui.refreshDebug(); this.icinga.ui.refreshDebug();
}, },

View File

@ -100,7 +100,7 @@
icinga.logger.info('Reloading CSS'); icinga.logger.info('Reloading CSS');
$('link').each(function() { $('link').each(function() {
var $oldLink = $(this); var $oldLink = $(this);
if ($oldLink.attr('type').indexOf('css') > -1) { if ($oldLink.hasAttr('type') && $oldLink.attr('type').indexOf('css') > -1) {
var $newLink = $oldLink.clone().attr( var $newLink = $oldLink.clone().attr(
'href', 'href',
icinga.utils.addUrlParams( icinga.utils.addUrlParams(

View File

@ -19,8 +19,8 @@ class SlidingwithborderTest extends BaseTestCase
$pages = $scrollingStyle->getPages($paginator); $pages = $scrollingStyle->getPages($paginator);
$this->assertInternalType('array', $pages); $this->assertInternalType('array', $pages);
$this->assertCount(13, $pages); $this->assertCount(10, $pages);
$this->assertEquals('...', $pages[11]); $this->assertEquals('...', $pages[8]);
} }
public function testGetPages3() public function testGetPages3()
@ -31,9 +31,9 @@ class SlidingwithborderTest extends BaseTestCase
$pages = $scrollingStyle->getPages($paginator); $pages = $scrollingStyle->getPages($paginator);
$this->assertInternalType('array', $pages); $this->assertInternalType('array', $pages);
$this->assertCount(16, $pages); $this->assertCount(10, $pages);
$this->assertEquals('...', $pages[3]); $this->assertEquals('...', $pages[3]);
$this->assertEquals('...', $pages[14]); $this->assertEquals('...', $pages[12]);
} }
protected function getPaginatorAdapter() protected function getPaginatorAdapter()

View File

@ -21,7 +21,7 @@ class UrlTest extends BaseTestCase
$url = Url::fromRequest(); $url = Url::fromRequest();
$this->assertEquals( $this->assertEquals(
'/path/to/my/test/url.html?param1=value1&amp;param2=value2', '/path/to/my/test/url.html?param1=value1&amp;param2=value2',
$url->getAbsoluteUrl(), $url->getAbsoluteUrl('&amp;'),
'Url::fromRequest does not reassemble the correct url from the global request' 'Url::fromRequest does not reassemble the correct url from the global request'
); );
} }
@ -119,7 +119,7 @@ class UrlTest extends BaseTestCase
*/ */
public function testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters() public function testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters()
{ {
$url = Url::fromPath('/my/test/url.html?param1=%25arg1&param2=arg+2' $url = Url::fromPath('/my/test/url.html?param1=%25arg1&param2=arg%202'
. '&param3[]=1&param3[]=2&param3[]=3&param4[key1]=val1&param4[key2]=val2'); . '&param3[]=1&param3[]=2&param3[]=3&param4[key1]=val1&param4[key2]=val2');
$this->assertEquals( $this->assertEquals(

View File

@ -0,0 +1,24 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Tests\Icinga\Regression;
use Icinga\Test\BaseTestCase;
use Icinga\Web\Url;
/**
* Regression-Test for bug #6284
*
* href-helper drops URL fragments.
*
* @see https://dev.icinga.org/issues/6284
*/
class Bug6284Test extends BaseTestCase
{
public function testWhetherUrlFromPathDoesNotDropFragments()
{
$url = 'some/route/with#anchor';
$this->assertEquals($url, Url::fromPath($url)->getRelativeUrl());
}
}