Merge branch 'master' into feature/deduplicate-puppet-code-6842

Conflicts:
	packages/rpm/etc/icingaweb2/resources.ini
This commit is contained in:
Alexander Klimov 2014-10-20 13:12:31 +02:00
commit 570b31fe05
351 changed files with 14561 additions and 16562 deletions
application
config
icingaweb2.spec
library/Icinga

@ -14,7 +14,6 @@ use Icinga\Exception\AuthenticationException;
use Icinga\Exception\NotReadableError;
use Icinga\Exception\ConfigurationError;
use Icinga\User;
use Icinga\Web\Session;
use Icinga\Web\Url;
/**
@ -36,7 +35,6 @@ class AuthenticationController extends ActionController
{
$auth = $this->Auth();
$this->view->form = $form = new LoginForm();
$form->setRequest($this->_request);
$this->view->title = $this->translate('Icingaweb Login');
try {
@ -55,27 +53,17 @@ class AuthenticationController extends ActionController
$config = Config::app('authentication');
} catch (NotReadableError $e) {
throw new ConfigurationError(
$this->translate(
'Could not read your authentiction.ini, no authentication methods are available.'
)
$this->translate('Could not read your authentiction.ini, no authentication methods are available.'),
0,
$e
);
}
$chain = new AuthChain($config);
if ($this->getRequest()->isGet()) {
$user = new User('');
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
$authenticated = $backend->authenticate($user);
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->rerenderLayout()->redirectNow($redirectUrl);
}
}
}
} elseif ($form->isSubmittedAndValid()) {
$user = new User($form->getValue('username'));
$password = $form->getValue('password');
$request = $this->getRequest();
if ($request->isPost() && $this->view->form->isValid($request->getPost())) {
$user = new User($this->view->form->getValue('username'));
$password = $this->view->form->getValue('password');
$backendsTried = 0;
$backendsWithError = 0;
@ -121,14 +109,27 @@ class AuthenticationController extends ActionController
);
}
if ($backendsWithError) {
$form->addNote(
$this->view->form->getElement('username')->addError(
$this->translate(
'Please note that not all authentication methods where available.'
'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.'
)
);
}
$form->getElement('password')->addError($this->translate('Incorrect username or password'));
$this->view->form->getElement('password')->addError($this->translate('Incorrect username or password'));
} elseif ($request->isGet()) {
$user = new User('');
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
$authenticated = $backend->authenticate($user);
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->rerenderLayout()->redirectNow(
Url::fromPath(Url::fromRequest()->getParam('redirect', 'dashboard'))
);
}
}
}
}
} catch (Exception $e) {
$this->view->errorInfo = $e->getMessage();
@ -141,14 +142,16 @@ class AuthenticationController extends ActionController
public function logoutAction()
{
$auth = $this->Auth();
if (! $auth->isAuthenticated()) {
$this->redirectToLogin();
}
$isRemoteUser = $auth->getUser()->isRemoteUser();
$auth->removeAuthorization();
if ($isRemoteUser === true) {
$this->_helper->layout->setLayout('login');
$this->_response->setHttpResponseCode(401);
} else {
$this->rerenderLayout()->redirectToLogin();
$this->redirectToLogin();
}
}
}

@ -2,38 +2,25 @@
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Web\Controller\BaseConfigController;
use Icinga\Web\Widget\Tab;
use Icinga\Web\Widget\AlertMessageBox;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Notification;
use Icinga\Application\Modules\Module;
use Icinga\Web\Url;
use Icinga\Web\Widget;
use Icinga\Application\Icinga;
use Icinga\Application\Config as IcingaConfig;
use Icinga\Form\Config\GeneralConfigForm;
use Icinga\Form\Config\AuthenticationBackendReorderForm;
use Icinga\Form\Config\AuthenticationBackendConfigForm;
use Icinga\Form\Config\ResourceConfigForm;
use Icinga\Form\ConfirmRemovalForm;
use Icinga\Data\ResourceFactory;
use Icinga\Form\Config\GeneralForm;
use Icinga\Form\Config\Authentication\ReorderForm;
use Icinga\Form\Config\Authentication\LdapBackendForm;
use Icinga\Form\Config\Authentication\DbBackendForm;
use Icinga\Form\Config\ResourceForm;
use Icinga\Form\Config\LoggingForm;
use Icinga\Form\Config\ConfirmRemovalForm;
use Icinga\Config\PreservingIniWriter;
/**
* Application wide controller for application preferences
*/
class ConfigController extends BaseConfigController
class ConfigController extends ActionController
{
/**
* The resource types that are available.
*
* @var array
*/
private $resourceTypes = array('livestatus', 'ido', 'statusdat', 'ldap');
public function init()
{
$this->view->tabs = Widget::create('tabs')->add('index', array(
@ -45,9 +32,6 @@ class ConfigController extends BaseConfigController
))->add('resources', array(
'title' => 'Resources',
'url' => 'config/resource'
))->add('logging', array(
'title' => 'Logging',
'url' => 'config/logging'
));
}
@ -61,42 +45,12 @@ class ConfigController extends BaseConfigController
*/
public function indexAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$form = new GeneralConfigForm();
$form->setIniConfig(IcingaConfig::app());
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('index');
$form = new GeneralForm();
$form->setConfiguration(IcingaConfig::app());
$form->setRequest($this->_request);
if ($form->isSubmittedAndValid()) {
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
return;
}
Notification::success('New configuration has successfully been stored');
$form->setConfiguration(IcingaConfig::app(), true);
$this->redirectNow('config/index');
}
$this->view->form = $form;
}
/**
* Form for modifying the logging configuration
*/
public function loggingAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$this->view->tabs->activate('logging');
$form = new LoggingForm();
$form->setConfiguration(IcingaConfig::app());
$form->setRequest($this->_request);
if ($form->isSubmittedAndValid()) {
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
return;
}
Notification::success('New configuration has sucessfully been stored');
$form->setConfiguration(IcingaConfig::app(), true);
$this->redirectNow('config/logging');
}
$this->view->form = $form;
}
/**
@ -142,7 +96,7 @@ class ConfigController extends BaseConfigController
try {
$manager->enableModule($module);
$manager->loadModule($module);
Notification::success('Module "' . $module . '" enabled');
Notification::success(sprintf($this->translate('Module "%s" enabled'), $module));
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
} catch (Exception $e) {
$this->view->exceptionMesssage = $e->getMessage();
@ -161,7 +115,7 @@ class ConfigController extends BaseConfigController
$manager = Icinga::app()->getModuleManager();
try {
$manager->disableModule($module);
Notification::success('Module "' . $module . '" disabled');
Notification::success(sprintf($this->translate('Module "%s" disabled'), $module));
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
} catch (Exception $e) {
$this->view->exceptionMessage = $e->getMessage();
@ -172,40 +126,17 @@ class ConfigController extends BaseConfigController
}
/**
* Action for creating a new authentication backend
* Action for listing and reordering authentication backends
*/
public function authenticationAction()
{
$config = IcingaConfig::app('authentication', true);
$form = new AuthenticationBackendReorderForm();
$form->setIniConfig(IcingaConfig::app('authentication'));
$form->handleRequest();
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$order = array_keys($config->toArray());
$this->view->messageBox = new AlertMessageBox(true);
$backends = array();
foreach ($order as $backend) {
$form = new ReorderForm();
$form->setName('form_reorder_backend_' . $backend);
$form->setBackendName($backend);
$form->setCurrentOrder($order);
$form->setRequest($this->_request);
if ($form->isSubmittedAndValid()) {
if ($this->writeAuthenticationFile($form->getReorderedConfig($config))) {
Notification::success('Authentication Order Updated');
$this->redirectNow('config/authentication');
}
return;
}
$backends[] = (object) array(
'name' => $backend,
'reorderForm' => $form
);
}
$this->view->backends = $backends;
$this->render('authentication/reorder');
}
/**
@ -213,316 +144,151 @@ class ConfigController extends BaseConfigController
*/
public function createauthenticationbackendAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$form = new AuthenticationBackendConfigForm();
$form->setIniConfig(IcingaConfig::app('authentication'));
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
$form->setRedirectUrl('config/authentication');
$form->handleRequest();
if ($this->getRequest()->getParam('type') === 'ldap') {
$form = new LdapBackendForm();
} else {
$form = new DbBackendForm();
}
if ($this->getParam('auth_backend')) {
$form->setBackendName($this->getParam('auth_backend'));
}
$form->setRequest($this->getRequest());
if ($form->isSubmittedAndValid()) {
$backendCfg = IcingaConfig::app('authentication')->toArray();
foreach ($backendCfg as $backendName => $settings) {
unset($backendCfg[$backendName]['name']);
}
foreach ($form->getConfig() as $backendName => $settings) {
unset($settings->{'name'});
if (isset($backendCfg[$backendName])) {
$this->addErrorMessage('Backend name already exists');
$this->view->form = $form;
$this->render('authentication/create');
return;
}
$backendCfg[$backendName] = $settings;
}
if ($this->writeAuthenticationFile($backendCfg)) {
// redirect to overview with success message
Notification::success('Backend Modification Written.');
$this->redirectNow("config/authentication");
}
return;
}
$this->view->messageBox->addForm($form);
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$this->render('authentication/create');
}
/**
* Form for editing backends
*
* Mostly the same like the createAuthenticationBackendAction, but with additional checks for backend existence
* and form population
* Action for editing authentication backends
*/
public function editauthenticationbackendAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$form = new AuthenticationBackendConfigForm();
$form->setIniConfig(IcingaConfig::app('authentication'));
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
$form->setRedirectUrl('config/authentication');
$form->handleRequest();
$configArray = IcingaConfig::app('authentication', true)->toArray();
$authBackend = $this->getParam('auth_backend');
if (!isset($configArray[$authBackend])) {
$this->addErrorMessage('Can\'t edit: Unknown Authentication Backend Provided');
$this->configurationerrorAction();
return;
}
if (!array_key_exists('resource', $configArray[$authBackend])) {
$this->addErrorMessage('Configuration error: Backend "' . $authBackend . '" has no Resource');
$this->configurationerrorAction();
return;
}
$type = ResourceFactory::getResourceConfig($configArray[$authBackend]['resource'])->type;
switch ($type) {
case 'ldap':
$form = new LdapBackendForm();
break;
case 'db':
$form = new DbBackendForm();
break;
default:
$this->addErrorMessage('Can\'t edit: backend type "' . $type . '" of given resource not supported.');
$this->configurationerrorAction();
return;
}
$form->setBackendName($this->getParam('auth_backend'));
$form->setBackend(IcingaConfig::app('authentication', true)->$authBackend);
$form->setRequest($this->getRequest());
if ($form->isSubmittedAndValid()) {
$backendCfg = IcingaConfig::app('authentication')->toArray();
foreach ($form->getConfig() as $backendName => $settings) {
$backendCfg[$backendName] = $settings;
// Remove the old section if the backend is renamed
if ($backendName != $authBackend) {
unset($backendCfg[$authBackend]);
}
unset($settings['name']);
}
if ($this->writeAuthenticationFile($backendCfg)) {
// redirect to overview with success message
Notification::success('Backend "' . $authBackend . '" created');
$this->redirectNow("config/authentication");
}
return;
}
$this->view->messageBox->addForm($form);
$this->view->name = $authBackend;
$this->view->form = $form;
$this->view->tabs->activate('authentication');
$this->render('authentication/modify');
}
/**
* Action for removing a backend from the authentication list.
*
* Redirects to the overview after removal is finished
* Action for removing a backend from the authentication list
*/
public function removeauthenticationbackendAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$form = new ConfirmRemovalForm(array(
'onSuccess' => function ($request) {
$configForm = new AuthenticationBackendConfigForm();
$configForm->setIniConfig(IcingaConfig::app('authentication'));
$authBackend = $request->getQuery('auth_backend');
$configArray = IcingaConfig::app('authentication', true)->toArray();
$authBackend = $this->getParam('auth_backend');
if (!isset($configArray[$authBackend])) {
Notification::error('Can\'t perform removal: Unknown Authentication Backend Provided');
$this->render('authentication/remove');
return;
}
try {
$configForm->remove($authBackend);
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
}
$form = new ConfirmRemovalForm();
$form->setRequest($this->getRequest());
$form->setRemoveTarget('auth_backend', $authBackend);
if ($form->isSubmittedAndValid()) {
unset($configArray[$authBackend]);
if ($this->writeAuthenticationFile($configArray)) {
Notification::success('Authentication Backend "' . $authBackend . '" Removed');
$this->redirectNow("config/authentication");
if ($configForm->save()) {
Notification::success(sprintf(
t('Authentication backend "%s" has been successfully removed'),
$authBackend
));
} else {
return false;
}
}
return;
}
));
$form->setRedirectUrl('config/authentication');
$form->handleRequest();
$this->view->form = $form;
$this->view->name = $authBackend;
$this->view->tabs->activate('authentication');
$this->render('authentication/remove');
}
public function resourceAction($showOnly = false)
/**
* Display all available resources and a link to create a new one and to remove existing ones
*/
public function resourceAction()
{
$this->view->tabs->activate('resources');
$this->view->messageBox = new AlertMessageBox(true);
$this->view->resources = IcingaConfig::app('resources', true)->toArray();
$this->render('resource');
$this->view->tabs->activate('resources');
}
/**
* Display a form to create a new resource
*/
public function createresourceAction()
{
$this->view->resourceTypes = $this->resourceTypes;
$resources = IcingaConfig::app('resources', true);
$form = new ResourceForm();
$form->setRequest($this->_request);
if ($form->isSubmittedAndValid()) {
$name = $form->getName();
if (isset($resources->{$name})) {
$this->addErrorMessage('Resource name "' . $name .'" already in use.');
} else {
$resources->{$name} = $form->getConfig();
if ($this->writeConfigFile($resources, 'resources')) {
$this->addSuccessMessage('Resource "' . $name . '" created.');
$this->redirectNow("config/resource");
}
}
}
$form = new ResourceConfigForm();
$form->setIniConfig(IcingaConfig::app('resources'));
$form->setRedirectUrl('config/resource');
$form->handleRequest();
$this->view->messageBox = new AlertMessageBox(true);
$this->view->messageBox->addForm($form);
$this->view->form = $form;
$this->render('resource/create');
}
/**
* Display a form to edit a existing resource
*/
public function editresourceAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$form = new ResourceConfigForm();
$form->setIniConfig(IcingaConfig::app('resources'));
$form->setRedirectUrl('config/resource');
$form->handleRequest();
$resources = ResourceFactory::getResourceConfigs();
$name = $this->getParam('resource');
if ($resources->get($name) === null) {
$this->addErrorMessage('Can\'t edit: Unknown Resource Provided');
$this->render('resource/modify');
return;
}
$form = new ResourceForm();
if ($this->_request->isPost() === false) {
$form->setOldName($name);
$form->setName($name);
}
$form->setRequest($this->_request);
$form->setResource($resources->get($name));
if ($form->isSubmittedAndValid()) {
$oldName = $form->getOldName();
$name = $form->getName();
if ($oldName !== $name) {
unset($resources->{$oldName});
}
$resources->{$name} = $form->getConfig();
if ($this->writeConfigFile($resources, 'resources')) {
$this->addSuccessMessage('Resource "' . $name . '" edited.');
$this->redirectNow("config/resource");
}
return;
}
$this->view->messageBox->addForm($form);
$this->view->form = $form;
$this->view->name = $name;
$this->render('resource/modify');
}
/**
* Display a confirmation form to remove a resource
*/
public function removeresourceAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$form = new ConfirmRemovalForm(array(
'onSuccess' => function ($request) {
$configForm = new ResourceConfigForm();
$configForm->setIniConfig(IcingaConfig::app('resources'));
$resource = $request->getQuery('resource');
$resources = ResourceFactory::getResourceConfigs()->toArray();
$name = $this->getParam('resource');
if (!isset($resources[$name])) {
$this->addSuccessMessage('Can\'t remove: Unknown resource provided');
$this->render('resource/remove');
return;
}
try {
$configForm->remove($resource);
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
}
$form = new ConfirmRemovalForm();
$form->setRequest($this->getRequest());
$form->setRemoveTarget('resource', $name);
if ($configForm->save()) {
Notification::success(sprintf(t('Resource "%s" has been successfully removed'), $resource));
} else {
return false;
}
}
));
$form->setRedirectUrl('config/resource');
$form->handleRequest();
// Check if selected resource is currently used for authentication
$authConfig = IcingaConfig::app('authentication', true)->toArray();
$resource = $this->getRequest()->getQuery('resource');
$authConfig = IcingaConfig::app('authentication')->toArray();
foreach ($authConfig as $backendName => $config) {
if (array_key_exists('resource', $config) && $config['resource'] === $name) {
$this->addErrorMessage(
'Warning: The resource "' . $name . '" is currently used for user authentication by "' . $backendName . '". ' .
' Deleting it could eventally make login impossible.'
);
}
}
if ($form->isSubmittedAndValid()) {
unset($resources[$name]);
if ($this->writeConfigFile($resources, 'resources')) {
$this->addSuccessMessage('Resource "' . $name . '" removed.');
$this->redirectNow('config/resource');
if (array_key_exists('resource', $config) && $config['resource'] === $resource) {
$form->addError(sprintf(
$this->translate(
'The resource "%s" is currently in use by the authentication backend "%s". ' .
'Removing the resource can result in noone being able to log in any longer.'
),
$resource,
$backendName
));
}
return;
}
$this->view->name = $name;
$this->view->form = $form;
$this->render('resource/remove');
}
/**
* Redirect target only for error-states
*
* When an error is opened in the side-pane, redirecting this request to the index or the overview will look
* weird. This action returns a clear page containing only an AlertMessageBox.
*/
public function configurationerrorAction()
{
$this->view->messageBox = new AlertMessageBox(true);
$this->render('error/error', null, true);
}
/**
* Write changes to an authentication file
*
* @param array $config The configuration changes
*
* @return bool True when persisting succeeded, otherwise false
*
* @see writeConfigFile()
*/
private function writeAuthenticationFile($config) {
return $this->writeConfigFile($config, 'authentication');
}
/**
* Write changes to a configuration file $file, using the supplied writer or PreservingIniWriter if none is set
*
* @param array|Zend_Config $config The configuration to write
* @param string $file The filename to write to (without .ini)
* @param Zend_Config_Writer $writer An optional writer to use for persisting changes
*
* @return bool True when persisting succeeded, otherwise false
*/
private function writeConfigFile($config, $file, $writer = null)
{
if (is_array($config)) {
$config = new Zend_Config($config);
}
if ($writer === null) {
$writer = new PreservingIniWriter(
array(
'config' => $config,
'filename' => IcingaConfig::app($file)->getConfigFile()
)
);
}
try {
$writer->write();
return true;
} catch (Exception $exc) {
$this->view->exceptionMessage = $exc->getMessage();
$this->view->iniConfigurationString = $writer->render();
$this->view->file = $file;
$this->render('show-configuration');
return false;
}
}
}

@ -82,25 +82,29 @@ class DashboardController extends ActionController
)->activate('addurl');
$form = new AddUrlForm();
$form->setRequest($this->getRequest());
$form->setAction(Url::fromRequest()->setParams(array())->getAbsoluteUrl());
$this->view->form = $form;
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost()) && $form->isSubmitted()) {
$dashboard = $this->getDashboard();
$dashboard->setComponentUrl(
$form->getValue('pane'),
$form->getValue('component'),
ltrim($form->getValue('url'), '/')
);
if ($form->isSubmittedAndValid()) {
$dashboard = $this->getDashboard();
$dashboard->setComponentUrl(
$form->getValue('pane'),
$form->getValue('component'),
ltrim($form->getValue('url'), '/')
);
$configFile = IcingaConfig::app('dashboard/dashboard')->getConfigFile();
if ($this->writeConfiguration(new Zend_Config($dashboard->toArray()), $configFile)) {
$this->redirectNow(Url::fromPath('dashboard', array('pane' => $form->getValue('pane'))));
} else {
$this->render('show-configuration');
$configFile = IcingaConfig::app('dashboard/dashboard')->getConfigFile();
if ($this->writeConfiguration(new Zend_Config($dashboard->toArray()), $configFile)) {
$this->redirectNow(Url::fromPath('dashboard', array('pane' => $form->getValue('pane'))));
} else {
$this->render('showConfiguration');
return;
}
}
} else {
$form->create()->setDefault('url', htmlspecialchars_decode($request->getParam('url', '')));
}
$this->view->form = $form;
}
/**
@ -160,9 +164,9 @@ class DashboardController extends ActionController
$writer->write();
} catch (Exception $e) {
Logger::error(new ConfiguationError("Cannot write dashboard to $target", 0, $e));
$this->view->iniConfigurationString = $writer->render();
$this->view->exceptionMessage = $e->getMessage();
$this->view->file = $target;
$this->view->configString = $writer->render();
$this->view->errorMessage = $e->getMessage();
$this->view->filePath = $target;
return false;
}

@ -3,11 +3,12 @@
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Web\Controller\BasePreferenceController;
use Icinga\Web\Widget\Tab;
use Icinga\Application\Config as IcingaConfig;
use Icinga\Web\Url;
use Icinga\Form\Preference\GeneralForm;
use Icinga\Web\Notification;
use Icinga\Web\Widget\Tab;
use Icinga\Application\Config;
use Icinga\Form\PreferenceForm;
use Icinga\Exception\ConfigurationError;
use Icinga\User\Preferences\PreferencesStore;
/**
* Application wide preference controller for user preferences
@ -34,27 +35,22 @@ class PreferenceController extends BasePreferenceController
}
/**
* General settings for date and time
* Show form to adjust user preferences
*/
public function indexAction()
{
$form = new GeneralForm();
$this->getTabs()->activate('general');
$form->setConfiguration(IcingaConfig::app())
->setRequest($this->getRequest());
if ($form->isSubmittedAndValid()) {
try {
$this->savePreferences($form->getPreferences());
Notification::success(t('Preferences updated successfully'));
// Recreate form to show new values
// TODO(el): It must sufficient to call $form->populate(...)
$form = new GeneralForm();
$form->setConfiguration(IcingaConfig::app());
$form->setRequest($this->getRequest());
} catch (Exception $e) {
Notification::error(sprintf(t('Failed to persist preferences. (%s)'), $e->getMessage()));
}
$storeConfig = Config::app()->preferences;
if ($storeConfig === null) {
throw new ConfigurationError(t('You need to configure how to store preferences first.'));
}
$user = $this->getRequest()->getUser();
$form = new PreferenceForm();
$form->setPreferences($user->getPreferences());
$form->setStore(PreferencesStore::create($storeConfig, $user));
$form->handleRequest();
$this->view->form = $form;
$this->getTabs()->activate('general');
}
}

@ -13,36 +13,45 @@ use Icinga\Web\Url;
class LoginForm extends Form
{
/**
* Interface how the form should be created
* Initialize this login form
*/
protected function create()
public function init()
{
$url = Url::fromRequest()->without('renderLayout');
$this->setName('form_login');
$this->addElement('text', 'username', array(
'label' => t('Username'),
'placeholder' => t('Please enter your username...'),
'required' => true,
));
$redir = $this->addElement('hidden', 'redirect');
$redirectUrl = $url->shift('redirect');
if ($redirectUrl) {
$this->setDefault('redirect', $redirectUrl);
}
$this->setSubmitLabel(t('Login'));
}
$this->addElement('password', 'password', array(
'label' => t('Password'),
'placeholder' => t('...and your password'),
'required' => true
));
// TODO: We need a place to intercept filled forms before rendering
if ($this->getRequest()->getPost('username') !== null) {
$this->getElement('password')->setAttrib('class', 'autofocus');
} else {
$this->getElement('username')->setAttrib('class', 'autofocus');
}
$this->setAction((string) $url);
$this->setSubmitLabel('Login');
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'username',
array(
'required' => true,
'label' => t('Username'),
'placeholder' => t('Please enter your username...'),
'class' => false === isset($formData['username']) ? 'autofocus' : ''
)
);
$this->addElement(
'password',
'password',
array(
'required' => true,
'label' => t('Password'),
'placeholder' => t('...and your password'),
'class' => isset($formData['username']) ? 'autofocus' : ''
)
);
$this->addElement(
'hidden',
'redirect',
array(
'value' => Url::fromRequest()->getParam('redirect')
)
);
}
}

@ -0,0 +1,89 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Authentication;
use Zend_Validate_Callback;
use Icinga\Web\Form;
/**
* Form class for adding/modifying autologin authentication backends
*/
class AutologinBackendForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_autologin');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t('The name of this authentication backend'),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => 'The backend name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
);
$this->addElement(
'text',
'strip_username_regexp',
array(
'required' => true,
'label' => t('Backend Domain Pattern'),
'description' => t('The domain pattern of this authentication backend'),
'value' => '/\@[^$]+$/',
'validators' => array(
new Zend_Validate_Callback(function ($value) {
return @preg_match($value, '') !== false;
})
)
)
);
$this->addElement(
'hidden',
'backend',
array(
'required' => true,
'value' => 'autologin'
)
);
return $this;
}
/**
* Validate the configuration by creating a backend and requesting the user count
*
* Returns always true as autologin backends are just "passive" backends. (The webserver authenticates users.)
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public function isValidAuthenticationBackend(Form $form)
{
return true;
}
}

@ -1,164 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Authentication;
use Zend_Config;
use Zend_Form_Element_Checkbox;
use Icinga\Web\Form;
use Icinga\Data\ResourceFactory;
use Icinga\Web\Form\Decorator\HelpText;
/**
* Base form for authentication backend forms
*/
abstract class BaseBackendForm extends Form
{
/**
* The name of the backend currently displayed in this form
*
* Will be the section in the authentication.ini file
*
* @var string
*/
protected $backendName = '';
/**
* The backend configuration as a Zend_Config object
*
* @var Zend_Config
*/
protected $backend;
/**
* The resources to use instead of the factory provided ones (use for testing)
*
* @var Zend_Config
*/
protected $resources;
/**
* Set the name of the currently displayed backend
*
* @param string $name The name to be stored as the section when persisting
*/
public function setBackendName($name)
{
$this->backendName = $name;
}
/**
* Return the backend name of this form
*
* @return string
*/
public function getBackendName()
{
return $this->backendName;
}
/**
* Return the backend configuration or a empty Zend_Config object if none is given
*
* @return Zend_Config
*/
public function getBackend()
{
return ($this->backend !== null) ? $this->backend : new Zend_Config(array());
}
/**
* Set the backend configuration for initial population
*
* @param Zend_Config $backend The backend to display in this form
*/
public function setBackend(Zend_Config $backend)
{
$this->backend = $backend;
}
/**
* Set an alternative array of resources that should be used instead of the DBFactory resource set
*
* @param array $resources The resources to use for populating the db selection field
*/
public function setResources(array $resources)
{
$this->resources = $resources;
}
/**
* Return content of the resources.ini or previously set resources
*
* @return array
*/
public function getResources()
{
if ($this->resources === null) {
return ResourceFactory::getResourceConfigs()->toArray();
} else {
return $this->resources;
}
}
/**
* Add checkbox at the beginning of the form which allows to skip logic connection validation
*/
protected function addForceCreationCheckbox()
{
$checkbox = new Zend_Form_Element_Checkbox(
array(
'name' => 'backend_force_creation',
'label' => t('Force Changes'),
'helptext' => t('Check this box to enforce changes without connectivity validation'),
'order' => 0
)
);
$checkbox->addDecorator(new HelpText());
$this->addElement($checkbox);
}
/**
* Validate this form with the Zend validation mechanism and perform a logic validation of the connection.
*
* If logic validation fails, the 'backend_force_creation' checkbox is prepended to the form to allow users to
* skip the logic connection validation.
*
* @param array $data The form input to validate
*
* @return bool Whether validation succeeded or not
*/
public function isValid($data)
{
if (!parent::isValid($data)) {
return false;
}
if (isset($data['backend_force_creation']) && $data['backend_force_creation']) {
return true;
}
if (!$this->isValidAuthenticationBackend()) {
$this->addForceCreationCheckbox();
return false;
}
return true;
}
/**
* Return an array containing all sections defined by this form as the key and all settings
* as an key-value sub-array
*
* @return array
*/
abstract public function getConfig();
/**
* Validate the configuration state of this backend with the concrete authentication backend.
*
* An implementation should not throw any exception, but use the add/setErrorMessages method of
* Zend_Form. If the 'backend_force_creation' checkbox is set, this method won't be called.
*
* @return bool Whether validation succeeded or not
*/
abstract public function isValidAuthenticationBackend();
}

@ -5,123 +5,116 @@
namespace Icinga\Form\Config\Authentication;
use Exception;
use Zend_Config;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Data\ResourceFactory;
use Icinga\Authentication\DbConnection;
use Icinga\Authentication\Backend\DbUserBackend;
/**
* Form class for adding/modifying database authentication backends
*/
class DbBackendForm extends BaseBackendForm
class DbBackendForm extends Form
{
/**
* Return content of the resources.ini or previously set resources
* The database resource names the user can choose from
*
* @return array
* @var array
*/
public function getResources()
protected $resources;
/**
* Initialize this form
*/
public function init()
{
if ($this->resources === null) {
$res = ResourceFactory::getResourceConfigs('db')->toArray();
$this->setName('form_config_authbackend_db');
}
foreach (array_keys($res) as $key) {
$res[$key] = $key;
}
/**
* Set the resource names the user can choose from
*
* @param array $resources The resources to choose from
*
* @return self
*/
public function setResources(array $resources)
{
$this->resources = $resources;
return $this;
}
return $res;
} else {
return $this->resources;
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t('The name of this authentication provider'),
)
);
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => t('Database Connection'),
'description' => t('The database connection to use for authenticating with this provider'),
'multiOptions' => false === empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
)
);
$this->addElement(
'hidden',
'backend',
array(
'required' => true,
'value' => 'db'
)
);
return $this;
}
/**
* Validate that the selected resource is a valid database authentication backend
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
if (false === $this->isValidAuthenticationBackend($this)) {
return false;
}
}
/**
* Create this form and add all required elements
* Validate the configuration by creating a backend and requesting the user count
*
* @see Form::create()
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public function create()
public function isValidAuthenticationBackend(Form $form)
{
$this->setName('form_modify_backend');
$name = $this->filterName($this->getBackendName());
$this->addElement(
'text',
'backend_' . $name . '_name',
array(
'required' => true,
'allowEmpty' => false,
'label' => t('Backend Name'),
'helptext' => t('The name of this authentication provider'),
'value' => $this->getBackendName()
)
);
$element = $form->getElement('resource');
$this->addElement(
'select',
'backend_' . $name . '_resource',
array(
'required' => true,
'allowEmpty' => false,
'label' => t('Database Connection'),
'helptext' => t('The database connection to use for authenticating with this provider'),
'value' => $this->getBackend()->get('resource'),
'multiOptions' => $this->getResources()
)
);
$this->addElement(
'button',
'btn_submit',
array(
'type' => 'submit',
'value' => '1',
'escape' => false,
'class' => 'btn btn-cta btn-wide',
'label' => '<i class="icinga-icon-save"></i> Save Backend'
)
);
}
/**
* Return the datatbase authentication backend configuration for this form
*
* @return array
*
* @see BaseBackendForm::getConfig()
*/
public function getConfig()
{
$prefix = 'backend_' . $this->filterName($this->getBackendName()) . '_';
$section = $this->getValue($prefix . 'name');
$cfg = array(
'backend' => 'db',
'resource' => $this->getValue($prefix . 'resource'),
);
return array($section => $cfg);
}
/**
* Validate the current configuration by creating a backend and requesting the user count
*
* @return bool Whether validation succeeded or not
*
* @see BaseBackendForm::isValidAuthenticationBackend
*/
public function isValidAuthenticationBackend()
{
try {
$dbUserBackend = new DbUserBackend(ResourceFactory::create(
$this->getValue('backend_' . $this->filterName($this->getBackendName()) . '_resource')
));
$dbUserBackend = new DbUserBackend(ResourceFactory::create($element->getValue()));
if ($dbUserBackend->count() < 1) {
$this->addErrorMessage(t("No users found under the specified database backend"));
$element->addError(t('No users found under the specified database backend'));
return false;
}
} catch (Exception $e) {
$this->addErrorMessage(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
$element->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
return false;
}
return true;
}
}

@ -5,166 +5,134 @@
namespace Icinga\Form\Config\Authentication;
use Exception;
use Icinga\Application\Platform;
use Zend_Config;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Data\ResourceFactory;
use Icinga\Authentication\Backend\LdapUserBackend;
/**
* Form for adding or modifying LDAP authentication backends
* Form class for adding/modifying LDAP authentication backends
*/
class LdapBackendForm extends BaseBackendForm
class LdapBackendForm extends Form
{
/**
* Return content of the resources.ini or previously set resources
* The ldap resource names the user can choose from
*
* @return array
* @var array
*/
public function getResources()
protected $resources;
/**
* Initialize this form
*/
public function init()
{
if ($this->resources === null) {
$res = ResourceFactory::getResourceConfigs('ldap')->toArray();
foreach (array_keys($res) as $key) {
$res[$key] = $key;
}
return $res;
} else {
return $this->resources;
}
$this->setName('form_config_authbackend_ldap');
}
/**
* Create this form and add all required elements
* Set the resource names the user can choose from
*
* @see Form::create()
* @param array $resources The resources to choose from
*
* @return self
*/
public function create()
public function setResources(array $resources)
{
$this->setName('form_modify_backend');
$name = $this->filterName($this->getBackendName());
$backend = $this->getBackend();
$this->resources = $resources;
return $this;
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'backend_' . $name . '_name',
'name',
array(
'required' => true,
'allowEmpty' => false,
'label' => t('Backend Name'),
'helptext' => t('The name of this authentication backend'),
'value' => $this->getBackendName()
'description' => t('The name of this authentication backend')
)
);
$this->addElement(
'select',
'backend_' . $name . '_resource',
'resource',
array(
'required' => true,
'allowEmpty' => false,
'label' => t('LDAP Resource'),
'helptext' => t('The resource to use for authenticating with this provider'),
'value' => $this->getBackend()->get('resource'),
'multiOptions' => $this->getResources()
'description' => t('The resource to use for authenticating with this provider'),
'multiOptions' => false === empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
)
);
$this->addElement(
'text',
'backend_' . $name . '_user_class',
'user_class',
array(
'required' => true,
'label' => t('LDAP User Object Class'),
'helptext' => t('The object class used for storing users on the ldap server'),
'value' => $backend->get('user_class', 'inetOrgPerson')
'required' => true,
'label' => t('LDAP User Object Class'),
'description' => t('The object class used for storing users on the ldap server'),
'value' => 'inetOrgPerson'
)
);
$this->addElement(
'text',
'backend_' . $name . '_user_name_attribute',
'user_name_attribute',
array(
'required' => true,
'label' => t('LDAP User Name Attribute'),
'description' => t('The attribute name used for storing the user name on the ldap server'),
'value' => 'uid'
)
);
$this->addElement(
'hidden',
'backend',
array(
'required' => true,
'label' => t('LDAP User Name Attribute'),
'helptext' => t('The attribute name used for storing the user name on the ldap server'),
'value' => $backend->get('user_name_attribute', 'uid')
'value' => 'ldap'
)
);
$this->addElement(
'button',
'btn_submit',
array(
'type' => 'submit',
'value' => '1',
'escape' => false,
'class' => 'btn btn-cta btn-wide',
'label' => '<i class="icinga-icon-save"></i> Save Backend'
)
);
return $this;
}
/**
* Return the ldap authentication backend configuration for this form
* Validate that the selected resource is a valid ldap authentication backend
*
* @return array
*
* @see BaseBackendForm::getConfig()
* @see Form::onSuccess()
*/
public function getConfig()
public function onSuccess(Request $request)
{
$prefix = 'backend_' . $this->filterName($this->getBackendName()) . '_';
$section = $this->getValue($prefix . 'name');
$cfg = array(
'backend' => 'ldap',
'resource' => $this->getValue($prefix . 'resource'),
'user_class' => $this->getValue($prefix . 'user_class'),
'user_name_attribute' => $this->getValue($prefix . 'user_name_attribute')
);
return array($section => $cfg);
}
/**
* Validate the current configuration by creating a backend and requesting the user count
*
* @return bool Whether validation succeeded or not
*
* @see BaseBackendForm::isValidAuthenticationBacken
*/
public function isValidAuthenticationBackend()
{
if (! Platform::extensionLoaded('ldap')) {
/*
* It should be possible to run icingaweb without the php ldap extension, when
* no ldap backends are needed. When the user tries to create an ldap backend
* without ldap installed we need to show him an error.
*/
$this->addErrorMessage(t('Using ldap is not possible, the php extension "ldap" is not installed.'));
if (false === $this->isValidAuthenticationBackend($this)) {
return false;
}
}
/**
* Validate the configuration by creating a backend and requesting the user count
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public function isValidAuthenticationBackend(Form $form)
{
$element = $form->getElement('resource');
try {
$cfg = $this->getConfig();
$backendName = 'backend_' . $this->filterName($this->getBackendName()) . '_name';
$backendConfig = new Zend_Config($cfg[$this->getValue($backendName)]);
$backend = ResourceFactory::create($backendConfig->resource);
$testConn = new LdapUserBackend(
$backend,
$backendConfig->user_class,
$backendConfig->user_name_attribute
);
$testConn->assertAuthenticationPossible();
/*
if ($testConn->count() === 0) {
throw new IcingaException('No Users Found On Directory Server');
}
*/
} catch (Exception $exc) {
$this->addErrorMessage(
t('Connection Validation Failed: ' . $exc->getMessage())
$ldapUserBackend = new LdapUserBackend(
ResourceFactory::create($element->getValue()),
$form->getElement('user_class')->getValue(),
$form->getElement('user_name_attribute')->getValue()
);
$ldapUserBackend->assertAuthenticationPossible();
} catch (Exception $e) {
$element->addError(sprintf(t('Connection validation failed: %s'), $e->getMessage()));
return false;
}

@ -1,233 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Authentication;
use Zend_Config;
use Icinga\Web\Form;
/**
* Form for modifying the authentication provider order
*/
class ReorderForm extends Form
{
/**
* The name of the current backend which will get action buttons for up and down movement
*
* @var string
*/
protected $backend;
/**
* The current ordering of all backends, required to determine possible changes
*
* @var array
*/
protected $currentOrder = array();
/**
* Set an array with the current order of all backends
*
* @param array $order An array containing backend names in the order
* they are defined in the authentication.ini
*/
public function setCurrentOrder(array $order)
{
$this->currentOrder = $order;
}
/**
* Set the name of the authentication backend for which to create the form
*
* @param string $backend The name of the authentication backend
*/
public function setBackendName($backend)
{
$this->backend = $backend;
}
/**
* Return the name of the currently set backend as it will appear in the form
*
* @return string The name of the backend
*/
public function getBackendName()
{
return $this->filterName($this->backend);
}
/**
* Create this form
*
* @see Form::create
*/
public function create()
{
if ($this->moveElementUp($this->backend, $this->currentOrder) !== $this->currentOrder) {
$upForm = new Form();
$upForm->addElement(
'hidden',
'form_backend_order',
array(
'required' => true,
'value' => join(',', $this->moveElementUp($this->backend, $this->currentOrder))
)
);
$upForm->addElement(
'button',
'btn_' . $this->getBackendName() . '_reorder_up',
array(
'type' => 'submit',
'escape' => false,
'value' => 'btn_' . $this->getBackendName() . '_reorder_up',
'name' => 'btn_' . $this->getBackendName() . '_reorder_up',
'label' => $this->getView()->icon('up.png', t('Move up in authentication order'))
)
);
$this->addSubForm($upForm, 'btn_reorder_up');
}
if ($this->moveElementDown($this->backend, $this->currentOrder) !== $this->currentOrder) {
$downForm = new Form();
$downForm->addElement(
'hidden',
'form_backend_order',
array(
'required' => true,
'value' => join(',', $this->moveElementDown($this->backend, $this->currentOrder))
)
);
$downForm->addElement(
'button',
'btn_' . $this->getBackendName() . '_reorder_down',
array(
'type' => 'submit',
'escape' => false,
'value' => 'btn_' . $this->getBackendName() . '_reorder_down',
'name' => 'btn_' . $this->getBackendName() . '_reorder_down',
'label' => $this->getView()->icon('down.png', t('Move down in authentication order'))
)
);
$this->addSubForm($downForm, 'btn_reorder_down');
}
}
/**
* Return the flattened result of $this->getValues
*
* @return array The currently set values
*
* @see Form::getValues()
*/
protected function getFlattenedValues()
{
$result = array();
foreach (parent::getValues() as $key => $value) {
if (is_array($value)) {
$result += $value;
} else {
$result[$key] = $value;
}
}
return $result;
}
/**
* Determine whether this form is submitted by testing the submit buttons of both subforms
*
* @return bool Whether the form has been submitted or not
*/
public function isSubmitted()
{
$checkData = $this->getRequest()->getParams();
return isset($checkData['btn_' . $this->getBackendName() . '_reorder_up']) ||
isset($checkData['btn_' . $this->getBackendName() . '_reorder_down']);
}
/**
* Return the reordered configuration after a reorder button has been submitted
*
* @param Zend_Config $config The configuration to reorder
*
* @return array An array containing the reordered configuration
*/
public function getReorderedConfig(Zend_Config $config)
{
$originalConfig = $config->toArray();
$newOrder = $this->getFlattenedValues();
$order = explode(',', $newOrder['form_backend_order']);
$reordered = array();
foreach ($order as $key) {
if (isset($originalConfig[$key])) {
$reordered[$key] = $originalConfig[$key];
}
}
return $reordered;
}
/**
* Static helper for moving an element in an array one slot up, if possible
*
* Example:
*
* <pre>
* $array = array('first', 'second', 'third');
* moveElementUp('third', $array); // returns ['first', 'third', 'second']
* </pre>
*
* @param string $key The key to bubble up one slot
* @param array $array The array to work with
*
* @return array The modified array
*/
protected static function moveElementUp($key, array $array)
{
for ($i = 0; $i < count($array) - 1; $i++) {
if ($array[$i + 1] === $key) {
$swap = $array[$i];
$array[$i] = $array[$i + 1];
$array[$i + 1] = $swap;
return $array;
}
}
return $array;
}
/**
* Static helper for moving an element in an array one slot down, if possible
*
* Example:
*
* <pre>
* $array = array('first', 'second', 'third');
* moveElementDown('first', $array); // returns ['second', 'first', 'third']
* </pre>
*
* @param string $key The key to bubble up one slot
* @param array $array The array to work with
*
* @return array The modified array
*/
protected static function moveElementDown($key, array $array)
{
for ($i = 0; $i < count($array) - 1; $i++) {
if ($array[$i] === $key) {
$swap = $array[$i + 1];
$array[$i + 1] = $array[$i];
$array[$i] = $swap;
return $array;
}
}
return $array;
}
}

@ -0,0 +1,322 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use InvalidArgumentException;
use Icinga\Web\Request;
use Icinga\Form\ConfigForm;
use Icinga\Web\Notification;
use Icinga\Application\Config;
use Icinga\Application\Platform;
use Icinga\Exception\ConfigurationError;
use Icinga\Form\Config\Authentication\DbBackendForm;
use Icinga\Form\Config\Authentication\LdapBackendForm;
use Icinga\Form\Config\Authentication\AutologinBackendForm;
class AuthenticationBackendConfigForm extends ConfigForm
{
/**
* The available resources split by type
*
* @var array
*/
protected $resources;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend');
$this->setSubmitLabel(t('Save Changes'));
}
/**
* Set the resource configuration to use
*
* @param Config $resources The resource configuration
*
* @return self
*
* @throws ConfigurationError In case no resources are available for authentication
*/
public function setResourceConfig(Config $resourceConfig)
{
$resources = array();
foreach ($resourceConfig as $name => $resource) {
$resources[strtolower($resource->type)][] = $name;
}
if (empty($resources)) {
throw new ConfigurationError(t('Could not find any resources for authentication'));
}
$this->resources = $resources;
return $this;
}
/**
* Return a form object for the given backend type
*
* @param string $type The backend type for which to return a form
*
* @return Form
*/
public function getBackendForm($type)
{
if ($type === 'db') {
$form = new DbBackendForm();
$form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array());
} elseif ($type === 'ldap') {
$form = new LdapBackendForm();
$form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array());
} elseif ($type === 'autologin') {
$form = new AutologinBackendForm();
} else {
throw new InvalidArgumentException(sprintf(t('Invalid backend type "%s" provided'), $type));
}
return $form;
}
/**
* Add a particular authentication backend
*
* The backend to add is identified by the array-key `name'.
*
* @param array $values The values to extend the configuration with
*
* @return self
*
* @throws InvalidArgumentException In case the backend does already exist
*/
public function add(array $values)
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
} elseif ($this->config->get($name) !== null) {
throw new InvalidArgumentException(t('Authentication backend already exists'));
}
unset($values['name']);
$this->config->{$name} = $values;
return $this;
}
/**
* Edit a particular authentication backend
*
* @param string $name The name of the backend to edit
* @param array $values The values to edit the configuration with
*
* @return array The edited backend configuration
*
* @throws InvalidArgumentException In case the backend does not exist
*/
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(t('Old authentication backend name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(t('New authentication backend name missing'));
} elseif (($backendConfig = $this->config->get($name)) === null) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
}
if ($newName !== $name) {
// Only remove the old entry if it has changed as the order gets screwed when editing backend names
unset($this->config->{$name});
}
unset($values['name']);
$this->config->{$newName} = array_merge($backendConfig->toArray(), $values);
return $this->config->{$newName};
}
/**
* Remove the given authentication backend
*
* @param string $name The name of the backend to remove
*
* @return array The removed backend configuration
*
* @throws InvalidArgumentException In case the backend does not exist
*/
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
} elseif (($backendConfig = $this->config->get($name)) === null) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
}
unset($this->config->{$name});
return $backendConfig;
}
/**
* Move the given authentication backend up or down in order
*
* @param string $name The name of the backend to be moved
* @param int $position The new (absolute) position of the backend
*
* @return self
*
* @throws InvalidArgumentException In case the backend does not exist
*/
public function move($name, $position)
{
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
} elseif ($this->config->get($name) === null) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
}
$backendOrder = $this->config->keys();
array_splice($backendOrder, array_search($name, $backendOrder), 1);
array_splice($backendOrder, $position, 0, $name);
$newConfig = array();
foreach ($backendOrder as $backendName) {
$newConfig[$backendName] = $this->config->get($backendName);
}
$config = new Config($newConfig);
$this->config = $config->setConfigFile($this->config->getConfigFile());
return $this;
}
/**
* Add or edit an authentication backend and save the configuration
*
* Performs a connectivity validation using the submitted values. A checkbox is
* added to the form to skip the check if it fails and redirection is aborted.
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
$backendForm = $this->getBackendForm($this->getElement('type')->getValue());
if (false === $backendForm->isValidAuthenticationBackend($this)) {
$this->addElement($this->getForceCreationCheckbox());
return false;
}
}
$authBackend = $request->getQuery('auth_backend');
try {
if ($authBackend === null) { // create new backend
$this->add($this->getValues());
$message = t('Authentication backend "%s" has been successfully created');
} else { // edit existing backend
$this->edit($authBackend, $this->getValues());
$message = t('Authentication backend "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
}
if ($this->save()) {
Notification::success(sprintf($message, $this->getElement('name')->getValue()));
} else {
return false;
}
}
/**
* Populate the form in case an authentication backend is being edited
*
* @see Form::onRequest()
*
* @throws ConfigurationError In case the backend name is missing in the request or is invalid
*/
public function onRequest(Request $request)
{
$authBackend = $request->getQuery('auth_backend');
if ($authBackend !== null) {
if ($authBackend === '') {
throw new ConfigurationError(t('Authentication backend name missing'));
} elseif (false === isset($this->config->{$authBackend})) {
throw new ConfigurationError(t('Unknown authentication backend provided'));
} elseif (false === isset($this->config->{$authBackend}->backend)) {
throw new ConfigurationError(sprintf(t('Backend "%s" has no `backend\' setting'), $authBackend));
}
$configValues = $this->config->{$authBackend}->toArray();
$configValues['type'] = $configValues['backend'];
$configValues['name'] = $authBackend;
$this->populate($configValues);
}
}
/**
* Return a checkbox to be displayed at the beginning of the form
* which allows the user to skip the connection validation
*
* @return Zend_Form_Element
*/
protected function getForceCreationCheckbox()
{
return $this->createElement(
'checkbox',
'force_creation',
array(
'order' => 0,
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
)
);
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$backendTypes = array();
$backendType = isset($formData['type']) ? $formData['type'] : 'db';
if (isset($this->resources['db'])) {
$backendTypes['db'] = t('Database');
}
if (isset($this->resources['ldap']) && ($backendType === 'ldap' || Platform::extensionLoaded('ldap'))) {
$backendTypes['ldap'] = 'LDAP';
}
$autologinBackends = array_filter(
$this->config->toArray(),
function ($authBackendCfg) {
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
}
);
if ($backendType === 'autologin' || empty($autologinBackends)) {
$backendTypes['autologin'] = t('Autologin');
}
$this->addElement(
'select',
'type',
array(
'ignore' => true,
'required' => true,
'autosubmit' => true,
'label' => t('Backend Type'),
'description' => t('The type of the resource to use for this authenticaton backend'),
'multiOptions' => $backendTypes
)
);
if (isset($formData['force_creation']) && $formData['force_creation']) {
// In case another error occured and the checkbox was displayed before
$this->addElement($this->getForceCreationCheckbox());
}
$this->addElements($this->getBackendForm($backendType)->createElements($formData)->getElements());
}
}

@ -0,0 +1,68 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use InvalidArgumentException;
use Icinga\Web\Request;
use Icinga\Web\Notification;
use Icinga\Form\ConfigForm;
class AuthenticationBackendReorderForm extends ConfigForm
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_reorder_authbackend');
$this->setViewScript('form/reorder-authbackend.phtml');
}
/**
* Return the ordered backend names
*
* @return array
*/
public function getBackendOrder()
{
return $this->config->keys();
}
/**
* Update the authentication backend order and save the configuration
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
$formData = $this->getRequestData($request);
if (isset($formData['backend_newpos'])) {
$configForm = $this->getConfigForm();
list($backendName, $position) = explode('|', $formData['backend_newpos'], 2);
try {
if ($configForm->move($backendName, $position)->save()) {
Notification::success(t('Authentication order updated!'));
} else {
return false;
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
}
}
}
/**
* Return the config form for authentication backends
*
* @return ConfigForm
*/
protected function getConfigForm()
{
$form = new AuthenticationBackendConfigForm();
$form->setIniConfig($this->config);
return $form;
}
}

@ -1,69 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use Icinga\Web\Form;
/**
* Form for confirming removal of an object
*/
class ConfirmRemovalForm extends Form
{
/**
* The value of the target to remove
*
* @var string
*/
private $removeTarget;
/**
* The name of the target parameter to remove
*
* @var string
*/
private $targetName;
/**
* Set the remove target in this field to be a hidden field with $name and value $target
*
* @param string $name The name to be set in the hidden field
* @param string $target The value to be set in the hidden field
*/
public function setRemoveTarget($name, $target)
{
$this->targetName = $name;
$this->removeTarget = $target;
}
/**
* Create the confirmation form
*
* @see Form::create()
*/
public function create()
{
$this->setName('form_confirm_removal');
$this->addElement(
'hidden',
$this->targetName,
array(
'value' => $this->removeTarget,
'required' => true
)
);
$this->addElement(
'button',
'btn_submit',
array(
'type' => 'submit',
'escape' => false,
'value' => '1',
'class' => 'btn btn-cta btn-common',
'label' => '<i class="icinga-icon-remove"></i> Confirm Removal'
)
);
}
}

@ -0,0 +1,118 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\General;
use DateTimeZone;
use Icinga\Web\Form;
use Icinga\Util\Translator;
use Icinga\Data\ResourceFactory;
/**
* Form class to modify the general application configuration
*/
class ApplicationConfigForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_general_application');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$languages = array();
foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language;
}
$this->addElement(
'select',
'global_language',
array(
'label' => t('Default Language'),
'required' => true,
'multiOptions' => $languages,
'description' => t(
'Select the language to use by default. Can be overwritten by a user in his preferences.'
)
)
);
$tzList = array();
foreach (DateTimeZone::listIdentifiers() as $tz) {
$tzList[$tz] = $tz;
}
$this->addElement(
'select',
'global_timezone',
array(
'label' => t('Default Application Timezone'),
'required' => true,
'multiOptions' => $tzList,
'description' => t(
'Select the timezone to be used as the default. User\'s can set their own timezone if'
. ' they like to, but this is the timezone to be used as the default setting .'
),
'value' => date_default_timezone_get()
)
);
$this->addElement(
'text',
'global_modulePath',
array(
'label' => t('Module Path'),
'required' => true,
'description' => t(
'Contains the directories that will be searched for available modules, separated by '
. 'colons. Modules that don\'t exist in these directories can still be symlinked in '
. 'the module folder, but won\'t show up in the list of disabled modules.'
),
'value' => realpath(ICINGAWEB_APPDIR . '/../modules')
)
);
$this->addElement(
'select',
'preferences_type',
array(
'required' => true,
'autosubmit' => true,
'label' => t('User Preference Storage Type'),
'multiOptions' => array(
'ini' => t('File System (INI Files)'),
'db' => t('Database'),
'null' => t('Don\'t Store Preferences')
)
)
);
if (isset($formData['preferences_type']) && $formData['preferences_type'] === 'db') {
$backends = array();
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
if ($resource['type'] === 'db') {
$backends[$name] = $name;
}
}
$this->addElement(
'select',
'preferences_resource',
array(
'required' => true,
'multiOptions' => $backends,
'label' => t('Database Connection')
)
);
}
return $this;
}
}

@ -0,0 +1,127 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\General;
use Icinga\Application\Icinga;
use Icinga\Logger\Logger;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\WritablePathValidator;
class LoggingConfigForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_general_logging');
}
/**
* (non-PHPDoc)
* @see Form::createElements() For the method documentation.
*/
public function createElements(array $formData)
{
$this->addElement(
'select',
'logging_log',
array(
'required' => true,
'class' => 'autosubmit',
'label' => t('Logging Type'),
'description' => t('The type of logging to utilize.'),
'multiOptions' => array(
'syslog' => 'Syslog',
'file' => t('File'),
'none' => t('None')
)
)
);
if (! isset($formData['logging_log']) || $formData['logging_log'] !== 'none') {
$this->addElement(
'select',
'logging_level',
array(
'required' => true,
'label' => t('Logging Level'),
'description' => t('The maximum logging level to emit.'),
'multiOptions' => array(
Logger::$levels[Logger::ERROR] => t('Error'),
Logger::$levels[Logger::WARNING] => t('Warning'),
Logger::$levels[Logger::INFO] => t('Information'),
Logger::$levels[Logger::DEBUG] => t('Debug')
)
)
);
}
if (isset($formData['logging_log']) && $formData['logging_log'] === 'syslog') {
$this->addElement(
'text',
'logging_application',
array(
'required' => true,
'label' => t('Application Prefix'),
'description' => t('The name of the application by which to prefix syslog messages.'),
'value' => 'icingaweb',
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\W]+$/',
'messages' => array(
'regexNotMatch' => 'The application prefix cannot contain any whitespaces.'
)
)
)
)
)
);
/*
* Note(el): Since we provide only one possible value for the syslog facility, I opt against exposing
* this configuration.
*/
// $this->addElement(
// 'select',
// 'logging_facility',
// array(
// 'required' => true,
// 'label' => t('Facility'),
// 'description' => t('The syslog facility to utilize.'),
// 'multiOptions' => array(
// 'user' => 'LOG_USER'
// )
// )
// );
} elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') {
$this->addElement(
'text',
'logging_file',
array(
'required' => true,
'label' => t('File path'),
'description' => t('The full path to the log file to write messages to.'),
'value' => $this->getDefaultLogDir(),
'validators' => array(new WritablePathValidator())
)
);
}
return $this;
}
/**
* Return the default logging directory for type 'file'
*
* @return string
*/
protected function getDefaultLogDir()
{
return realpath(Icinga::app()->getApplicationDir('../var/log/icingaweb.log'));
}
}

@ -0,0 +1,76 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use Icinga\Web\Request;
use Icinga\Web\Notification;
use Icinga\Form\ConfigForm;
use Icinga\Form\Config\General\LoggingConfigForm;
use Icinga\Form\Config\General\ApplicationConfigForm;
/**
* Form class for application-wide and logging specific settings
*/
class GeneralConfigForm extends ConfigForm
{
/**
* Initialize this configuration form
*/
public function init()
{
$this->setName('form_config_general');
$this->setSubmitLabel(t('Save Changes'));
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$appConfigForm = new ApplicationConfigForm();
$loggingConfigForm = new LoggingConfigForm();
$this->addElements($appConfigForm->createElements($formData)->getElements());
$this->addElements($loggingConfigForm->createElements($formData)->getElements());
}
/**
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
$sections = array();
foreach ($this->getValues() as $sectionAndPropertyName => $value) {
list($section, $property) = explode('_', $sectionAndPropertyName);
if (! isset($sections[$section])) {
$sections[$section] = array();
}
$sections[$section][$property] = $value;
}
foreach ($sections as $section => $config) {
$this->config->{$section} = $config;
}
if ($this->save()) {
Notification::success(t('New configuration has successfully been stored'));
} else {
return false;
}
}
/**
* @see Form::onRequest()
*/
public function onRequest(Request $request)
{
$values = array();
foreach ($this->config as $section => $properties) {
foreach ($properties as $name => $value) {
$values[$section . '_' . $name] = $value;
}
}
$this->populate($values);
}
}

@ -1,270 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use Icinga\Application\Config as IcingaConfig;
use Icinga\Data\ResourceFactory;
use Icinga\Web\Form;
use Icinga\Util\Translator;
use Icinga\Web\Form\Validator\WritablePathValidator;
use Icinga\Web\Form\Decorator\ConditionalHidden;
use DateTimeZone;
use Zend_Form_Element_Select;
use Zend_Config;
/**
* Configuration form for general, application-wide settings
*/
class GeneralForm extends Form
{
/**
* The base directory of the icingaweb configuration
*
* @var string
*/
private $configDir = null;
/**
* The resources to use instead of the factory provided ones (use for testing)
*
* @var null
*/
private $resources;
/**
* Set a specific configuration directory to use for configuration specific default paths
*
* @param string $dir
*/
public function setConfigDir($dir)
{
$this->configDir = $dir;
}
/**
* Return the config path set for this form or the application wide config path if none is set
*
* @return string
*
* @see IcingaConfig::configDir
*/
public function getConfigDir()
{
return $this->configDir === null ? IcingaConfig::$configDir : $this->configDir;
}
/**
* Set an alternative array of resources that should be used instead of the DBFactory resource set
* (used for testing)
*
* @param array $resources The resources to use for populating the db selection field
*/
public function setResources(array $resources)
{
$this->resources = $resources;
}
/**
* Return content of the resources.ini or previously set resources for displaying in the database selection field
*
* @return array
*/
public function getResources()
{
if ($this->resources === null) {
return ResourceFactory::getResourceConfigs()->toArray();
} else {
return $this->resources;
}
}
/**
* Add a select field for setting the default language
*
* Possible values are determined by Translator::getAvailableLocaleCodes.
*
* @param Zend_Config $cfg The "global" section of the config.ini
*/
private function addLanguageSelection(Zend_Config $cfg)
{
$languages = array();
foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language;
}
$this->addElement(
'select',
'language',
array(
'label' => t('Default Language'),
'required' => true,
'multiOptions' => $languages,
'helptext' => t(
'Select the language to use by default. Can be overwritten by a user in his preferences.'
),
'value' => $cfg->get('language', Translator::DEFAULT_LOCALE)
)
);
}
/**
* Add a select field for setting the default timezone.
*
* Possible values are determined by DateTimeZone::listIdentifiers
*
* @param Zend_Config $cfg The "global" section of the config.ini
*/
private function addTimezoneSelection(Zend_Config $cfg)
{
$tzList = array();
foreach (DateTimeZone::listIdentifiers() as $tz) {
$tzList[$tz] = $tz;
}
$helptext = 'Select the timezone to be used as the default. User\'s can set their own timezone if'
. ' they like to, but this is the timezone to be used as the default setting .';
$this->addElement(
'select',
'timezone',
array(
'label' => 'Default Application Timezone',
'required' => true,
'multiOptions' => $tzList,
'helptext' => $helptext,
'value' => $cfg->get('timezone', date_default_timezone_get())
)
);
}
/**
* Add configuration settings for module paths
*
* @param Zend_Config $cfg The "global" section of the config.ini
*/
private function addModuleSettings(Zend_Config $cfg)
{
$this->addElement(
'text',
'module_path',
array(
'label' => 'Module Path',
'required' => true,
'helptext' => 'Contains the directories that will be searched for available modules, separated by ' .
' colons. Modules that don\'t exist in these directories can still be symlinked in the module ' .
' folder, but won\'t show up in the list of disabled modules.',
'value' => $cfg->get('modulePath', realpath(ICINGAWEB_APPDIR . '/../modules'))
)
);
}
/**
* Add form elements for setting the user preference storage backend
*
* @param Zend_Config $cfg The Zend_config object of preference section
*/
public function addUserPreferencesDialog(Zend_Config $cfg)
{
$backend = $cfg->get('type', 'ini');
if ($this->getRequest()->get('preferences_type', null) !== null) {
$backend = $this->getRequest()->get('preferences_type');
}
$this->addElement(
'select',
'preferences_type',
array(
'label' => 'User Preference Storage Type',
'required' => true,
'value' => $backend,
'multiOptions' => array(
'ini' => 'File System (INI Files)',
'db' => 'Database',
'null' => 'Don\'t Store Preferences'
)
)
);
$backends = array();
foreach ($this->getResources() as $name => $resource) {
if ($resource['type'] !== 'db') {
continue;
}
$backends[$name] = $name;
}
$txtPreferencesDbResource = new Zend_Form_Element_Select(
array(
'name' => 'preferences_db_resource',
'label' => 'Database Connection',
'required' => $backend === 'db',
'condition' => $backend === 'db',
'value' => $cfg->get('resource'),
'multiOptions' => $backends
)
);
$validator = new WritablePathValidator();
$validator->setRequireExistence();
$this->addElement($txtPreferencesDbResource);
$txtPreferencesDbResource->addDecorator(new ConditionalHidden());
$this->enableAutoSubmit(
array(
'preferences_type'
)
);
}
/**
* Create the general form, using the provided configuration
*
* @see Form::create()
*/
public function create()
{
$config = $this->getConfiguration();
$global = $config->global;
if ($global === null) {
$global = new Zend_Config(array());
}
$preferences = $config->preferences;
if ($preferences === null) {
$preferences = new Zend_Config(array());
}
$this->setName('form_config_general');
$this->addLanguageSelection($global);
$this->addTimezoneSelection($global);
$this->addModuleSettings($global);
$this->addUserPreferencesDialog($preferences);
$this->setSubmitLabel('Save Changes');
}
/**
* Return an Zend_Config object containing the configuration set in this form
*
* @return Zend_Config
*/
public function getConfig()
{
$config = $this->getConfiguration();
if ($config->global === null) {
$config->global = new Zend_Config(array(), true);
}
if ($config->preferences === null) {
$config->preferences = new Zend_Config(array(), true);
}
$values = $this->getValues();
$cfg = clone $config;
$cfg->global->language = $values['language'];
$cfg->global->timezone = $values['timezone'];
$cfg->global->modulePath = $values['module_path'];
$cfg->preferences->type = $values['preferences_type'];
if ($cfg->preferences->type === 'db') {
$cfg->preferences->resource = $values['preferences_db_resource'];
}
return $cfg;
}
}

@ -1,178 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use Zend_Config;
use Icinga\Web\Form;
use Icinga\Application\Icinga;
use Icinga\Web\Form\Validator\WritablePathValidator;
/**
* Form class for setting the application wide logging configuration
*/
class LoggingForm extends Form
{
/**
* Return the default logging directory for type "file"
*
* @return string
*/
protected function getDefaultLogDir()
{
return realpath(Icinga::app()->getApplicationDir() . '/../var/log/icingaweb.log');
}
/**
* Create this logging configuration form
*
* @see Form::create()
*/
public function create()
{
$this->setName('form_config_logging');
$config = $this->getConfiguration();
if (($loggingConfig = $config->logging) === null) {
$loggingConfig = new Zend_Config(array());
}
$this->addElement(
'checkbox',
'logging_enable',
array(
'required' => true,
'label' => t('Logging Enabled'),
'helptext' => t('Check this to enable logging.'),
'value' => $loggingConfig->enable ? 1 : 0
)
);
$this->addElement(
'select',
'logging_level',
array(
'required' => true,
'label' => t('Logging Level'),
'helptext' => t('The maximum loglevel to emit.'),
'value' => intval($loggingConfig->get('level', 0)),
'multiOptions' => array(
0 => t('Error'),
1 => t('Warning'),
2 => t('Information'),
3 => t('Debug')
)
)
);
$this->addElement(
'select',
'logging_type',
array(
'required' => true,
'label' => t('Logging Type'),
'helptext' => t('The type of logging to utilize.'),
'value' => $loggingConfig->get('type', 'file'),
'multiOptions' => array(
'file' => t('File'),
'syslog' => 'Syslog'
)
)
);
$this->enableAutoSubmit(array('logging_type'));
switch ($this->getRequest()->getParam('logging_type', $loggingConfig->get('type', 'file')))
{
case 'syslog':
$this->addElement(
'text',
'logging_application',
array(
'required' => true,
'label' => t('Application Prefix'),
'helptext' => t('The name of the application by which to prefix syslog messages.'),
'value' => $loggingConfig->get('application', 'icingaweb'),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\W]+$/',
'messages' => array(
'regexNotMatch' => 'The application prefix cannot contain any whitespaces.'
)
)
)
)
)
);
$this->addElement(
'select',
'logging_facility',
array(
'required' => true,
'label' => t('Facility'),
'helptext' => t('The Syslog facility to utilize.'),
'value' => $loggingConfig->get('facility', 'LOG_USER'),
'multiOptions' => array(
'LOG_USER' => 'LOG_USER'
)
)
);
break;
case 'file':
default:
$this->addElement(
'text',
'logging_target',
array(
'required' => true,
'label' => t('Filepath'),
'helptext' => t('The logfile to write messages to.'),
'value' => $loggingConfig->target ? $loggingConfig->target : $this->getDefaultLogDir(),
'validators' => array(new WritablePathValidator())
)
);
}
$this->setSubmitLabel('{{SAVE_ICON}} Save Changes');
}
public function isValid($data) {
foreach ($this->getElements() as $key => $element) {
// Initialize all empty elements with their default values.
if (!isset($data[$key])) {
$data[$key] = $element->getValue();
}
}
return parent::isValid($data);
}
/**
* Return a Zend_Config object containing the state defined in this form
*
* @return Zend_Config The config defined in this form
*/
public function getConfig()
{
$values = $this->getValues();
$cfg = $this->getConfiguration()->toArray();
$cfg['logging']['enable'] = $values['logging_enable'] == 1;
$cfg['logging']['level'] = $values['logging_level'];
switch ($values['logging_type'])
{
case 'file':
$cfg['logging']['type'] = 'file';
$cfg['logging']['target'] = $values['logging_target'];
break;
case 'syslog':
$cfg['logging']['type'] = 'syslog';
$cfg['logging']['application'] = $values['logging_application'];
$cfg['logging']['facility'] = $values['logging_facility'];
break;
}
return new Zend_Config($cfg);
}
}

@ -0,0 +1,130 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Resource;
use Exception;
use Zend_Config;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Web\Form\Element\Number;
use Icinga\Data\ResourceFactory;
/**
* Form class for adding/modifying database resources
*/
class DbResourceForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_resource_db');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'select',
'db',
array(
'required' => true,
'label' => t('Database Type'),
'description' => t('The type of SQL database'),
'multiOptions' => array(
'mysql' => 'MySQL',
'pgsql' => 'PostgreSQL'
//'oracle' => 'Oracle'
)
)
);
$this->addElement(
'text',
'host',
array (
'required' => true,
'label' => t('Host'),
'description' => t('The hostname of the database'),
'value' => 'localhost'
)
);
$this->addElement(
new Number(
array(
'required' => true,
'name' => 'port',
'label' => t('Port'),
'description' => t('The port to use'),
'value' => 3306
)
)
);
$this->addElement(
'text',
'dbname',
array(
'required' => true,
'label' => t('Database Name'),
'description' => t('The name of the database to use')
)
);
$this->addElement(
'text',
'username',
array (
'required' => true,
'label' => t('Username'),
'description' => t('The user name to use for authentication')
)
);
$this->addElement(
'password',
'password',
array(
'required' => true,
'renderPassword' => true,
'label' => t('Password'),
'description' => t('The password to use for authentication')
)
);
return $this;
}
/**
* Validate that the current configuration points to a valid resource
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
if (false === $this->isValidResource($this)) {
return false;
}
}
/**
* Validate the resource configuration by trying to connect with it
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public function isValidResource(Form $form)
{
try {
$resource = ResourceFactory::createResource(new Zend_Config($form->getValues()));
$resource->getConnection()->getConnection();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
return false;
}
return true;
}
}

@ -0,0 +1,50 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Resource;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\ReadablePathValidator;
/**
* Form class for adding/modifying file resources
*/
class FileResourceForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_resource_file');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'filename',
array(
'required' => true,
'label' => t('Filepath'),
'description' => t('The filename to fetch information from'),
'validators' => array(new ReadablePathValidator())
)
);
$this->addElement(
'text',
'fields',
array(
'required' => true,
'label' => t('Pattern'),
'description' => t('The regular expression by which to identify columns')
)
);
return $this;
}
}

@ -0,0 +1,116 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Resource;
use Exception;
use Zend_Config;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Web\Form\Element\Number;
use Icinga\Data\ResourceFactory;
/**
* Form class for adding/modifying ldap resources
*/
class LdapResourceForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_resource_ldap');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'hostname',
array(
'required' => true,
'label' => t('Host'),
'description' => t('The hostname or address of the LDAP server to use for authentication'),
'value' => 'localhost'
)
);
$this->addElement(
new Number(
array(
'required' => true,
'name' => 'port',
'label' => t('Port'),
'description' => t('The port of the LDAP server to use for authentication'),
'value' => 389
)
)
);
$this->addElement(
'text',
'root_dn',
array(
'required' => true,
'label' => t('Root DN'),
'description' => t('The path where users can be found on the ldap server')
)
);
$this->addElement(
'text',
'bind_dn',
array(
'required' => true,
'label' => t('Bind DN'),
'description' => t('The user dn to use for querying the ldap server')
)
);
$this->addElement(
'password',
'bind_pw',
array(
'required' => true,
'renderPassword' => true,
'label' => t('Bind Password'),
'description' => t('The password to use for querying the ldap server')
)
);
return $this;
}
/**
* Validate that the current configuration points to a valid resource
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
if (false === $this->isValidResource($this)) {
return false;
}
}
/**
* Validate the resource configuration by trying to connect with it
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public function isValidResource(Form $form)
{
try {
$resource = ResourceFactory::createResource(new Zend_Config($form->getValues()));
$resource->connect();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
return false;
}
return true;
}
}

@ -0,0 +1,77 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config\Resource;
use Exception;
use Zend_Config;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Application\Icinga;
use Icinga\Data\ResourceFactory;
/**
* Form class for adding/modifying livestatus resources
*/
class LivestatusResourceForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_resource_livestatus');
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'socket',
array(
'required' => true,
'label' => t('Socket'),
'description' => t('The path to your livestatus socket used for querying monitoring data'),
'value' => realpath(Icinga::app()->getApplicationDir() . '/../var/rw/livestatus')
)
);
return $this;
}
/**
* Validate that the current configuration points to a valid resource
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
if (false === $this->isValidResource($this)) {
return false;
}
}
/**
* Validate the resource configuration by trying to connect with it
*
* @param Form $form The form to fetch the configuration values from
*
* @return bool Whether validation succeeded or not
*/
public function isValidResource(Form $form)
{
try {
$resource = ResourceFactory::createResource(new Zend_Config($form->getValues()));
$resource->connect()->disconnect();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
return false;
}
return true;
}
}

@ -0,0 +1,252 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use InvalidArgumentException;
use Icinga\Web\Request;
use Icinga\Web\Notification;
use Icinga\Form\ConfigForm;
use Icinga\Form\Config\Resource\DbResourceForm;
use Icinga\Form\Config\Resource\FileResourceForm;
use Icinga\Form\Config\Resource\LdapResourceForm;
use Icinga\Form\Config\Resource\LivestatusResourceForm;
use Icinga\Application\Platform;
use Icinga\Exception\ConfigurationError;
class ResourceConfigForm extends ConfigForm
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_resource');
$this->setSubmitLabel(t('Save Changes'));
}
/**
* Return a form object for the given resource type
*
* @param string $type The resource type for which to return a form
*
* @return Form
*/
public function getResourceForm($type)
{
if ($type === 'db') {
return new DbResourceForm();
} elseif ($type === 'ldap') {
return new LdapResourceForm();
} elseif ($type === 'livestatus') {
return new LivestatusResourceForm();
} elseif ($type === 'file') {
return new FileResourceForm();
} else {
throw new InvalidArgumentException(sprintf(t('Invalid resource type "%s" provided'), $type));
}
}
/**
* Add a particular resource
*
* The backend to add is identified by the array-key `name'.
*
* @param array $values The values to extend the configuration with
*
* @return self
*
* @thrwos InvalidArgumentException In case the resource does already exist
*/
public function add(array $values)
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(t('Resource name missing'));
} elseif ($this->config->{$name} !== null) {
throw new InvalidArgumentException(t('Resource already exists'));
}
unset($values['name']);
$this->config->{$name} = $values;
return $this;
}
/**
* Edit a particular resource
*
* @param string $name The name of the resource to edit
* @param array $values The values to edit the configuration with
*
* @return array The edited configuration
*
* @throws InvalidArgumentException In case the resource does not exist
*/
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(t('Old resource name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(t('New resource name missing'));
} elseif (($resourceConfig = $this->config->get($name)) === null) {
throw new InvalidArgumentException(t('Unknown resource provided'));
}
unset($values['name']);
unset($this->config->{$name});
$this->config->{$newName} = array_merge($resourceConfig->toArray(), $values);
return $this->config->{$newName};
}
/**
* Remove a particular resource
*
* @param string $name The name of the resource to remove
*
* @return array The removed resource configuration
*
* @throws InvalidArgumentException In case the resource does not exist
*/
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(t('Resource name missing'));
} elseif (($resourceConfig = $this->config->get($name)) === null) {
throw new InvalidArgumentException(t('Unknown resource provided'));
}
unset($this->config->{$name});
return $resourceConfig;
}
/**
* Add or edit a resource and save the configuration
*
* Performs a connectivity validation using the submitted values. A checkbox is
* added to the form to skip the check if it fails and redirection is aborted.
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
$resourceForm = $this->getResourceForm($this->getElement('type')->getValue());
if (method_exists($resourceForm, 'isValidResource') && false === $resourceForm->isValidResource($this)) {
$this->addElement($this->getForceCreationCheckbox());
return false;
}
}
$resource = $request->getQuery('resource');
try {
if ($resource === null) { // create new resource
$this->add($this->getValues());
$message = t('Resource "%s" has been successfully created');
} else { // edit existing resource
$this->edit($resource, $this->getValues());
$message = t('Resource "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
}
if ($this->save()) {
Notification::success(sprintf($message, $this->getElement('name')->getValue()));
} else {
return false;
}
}
/**
* Populate the form in case a resource is being edited
*
* @see Form::onRequest()
*
* @throws ConfigurationError In case the backend name is missing in the request or is invalid
*/
public function onRequest(Request $request)
{
$resource = $request->getQuery('resource');
if ($resource !== null) {
if ($resource === '') {
throw new ConfigurationError(t('Resource name missing'));
} elseif (false === isset($this->config->{$resource})) {
throw new ConfigurationError(t('Unknown resource provided'));
}
$configValues = $this->config->{$resource}->toArray();
$configValues['name'] = $resource;
$this->populate($configValues);
}
}
/**
* Return a checkbox to be displayed at the beginning of the form
* which allows the user to skip the connection validation
*
* @return Zend_Form_Element
*/
protected function getForceCreationCheckbox()
{
return $this->createElement(
'checkbox',
'force_creation',
array(
'order' => 0,
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
)
);
}
/**
* @see Form::createElemeents()
*/
public function createElements(array $formData)
{
$resourceType = isset($formData['type']) ? $formData['type'] : 'db';
$resourceTypes = array(
'file' => t('File'),
'livestatus' => 'Livestatus',
);
if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) {
$resourceTypes['ldap'] = 'LDAP';
}
if ($resourceType === 'db' || Platform::extensionLoaded('mysql') || Platform::extensionLoaded('pgsql')) {
$resourceTypes['db'] = t('SQL Database');
}
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
)
);
$this->addElement(
'select',
'type',
array(
'required' => true,
'autosubmit' => true,
'label' => t('Resource Type'),
'description' => t('The type of resource'),
'multiOptions' => $resourceTypes,
'value' => $resourceType
)
);
if (isset($formData['force_creation']) && $formData['force_creation']) {
// In case another error occured and the checkbox was displayed before
$this->addElement($this->getForceCreationCheckbox());
}
$this->addElements($this->getResourceForm($resourceType)->createElements($formData)->getElements());
}
}

@ -1,507 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Config;
use Exception;
use Icinga\Application\Platform;
use Zend_Config;
use Zend_Form_Element_Checkbox;
use Icinga\Web\Form;
use Icinga\Data\ResourceFactory;
use Icinga\Web\Form\Element\Number;
use Icinga\Web\Form\Decorator\HelpText;
class ResourceForm extends Form
{
/**
* The resource
*
* @var Zend_Config
*/
protected $resource;
/**
* The (new) name of the resource
*
* @var string
*/
protected $name;
/**
* The old name of the resource
*
* @var string
*/
protected $oldName;
/**
* Set the current resource name
*
* @param string $name The name to set
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get the current resource name
*
* @return null|string
*/
public function getName()
{
$name = $this->getValue('resource_all_name');
if (!$name) {
return $this->name;
}
return $name;
}
/**
* Set the original name of the resource
*
* @param string $name The name to set
*/
public function setOldName($name)
{
$this->oldName = $name;
}
/**
* Get the resource name that was initially set
*
* @return null|string
*/
public function getOldName()
{
$oldName = $this->getValue('resource_all_name_old');
if (!$oldName) {
return $this->oldName;
}
return $oldName;
}
/**
* Set the resource configuration to edit.
*
* @param Zend_Config $resource The config to set
*/
public function setResource(Zend_Config $resource)
{
$this->resource = $resource;
}
/**
* Get the current resource configuration.
*
* @return Zend_Config
*/
public function getResource()
{
if (!isset($this->resource)) {
$this->resource = new Zend_Config(array('type' => 'db'));
}
return $this->resource;
}
protected function addDbForm()
{
$this->addElement(
'select',
'resource_db_db',
array(
'required' => true,
'label' => t('Database Type'),
'helptext' => t('The type of SQL database you want to create.'),
'value' => $this->getResource()->get('db', 'mysql'),
'multiOptions' => array(
'mysql' => 'MySQL',
'pgsql' => 'PostgreSQL'
//'oracle' => 'Oracle'
)
)
);
$this->addElement(
'text',
'resource_db_host',
array (
'required' => true,
'label' => t('Host'),
'helptext' => t('The hostname of the database.'),
'value' => $this->getResource()->get('host', 'localhost')
)
);
$this->addElement(
new Number(
array(
'name' => 'resource_db_port',
'required' => true,
'label' => t('Port'),
'helptext' => t('The port to use.'),
'value' => $this->getResource()->get('port', 3306)
)
)
);
$this->addElement(
'text',
'resource_db_dbname',
array(
'required' => true,
'label' => t('Database Name'),
'helptext' => t('The name of the database to use'),
'value' => $this->getResource()->get('dbname', '')
)
);
$this->addElement(
'text',
'resource_db_username',
array (
'required' => true,
'label' => t('Username'),
'helptext' => t('The user name to use for authentication.'),
'value' => $this->getResource()->get('username', '')
)
);
$this->addElement(
'password',
'resource_db_password',
array(
'required' => true,
'renderPassword' => true,
'label' => t('Password'),
'helptext' => t('The password to use for authentication'),
'value' => $this->getResource()->get('password', '')
)
);
}
protected function addStatusdatForm()
{
$this->addElement(
'text',
'resource_statusdat_status_file',
array(
'required' => true,
'label' => t('Filepath'),
'helptext' => t('Location of your icinga status.dat file'),
'value' => $this->getResource()->get('status_file', '/usr/local/icinga/var/status.dat')
)
);
$this->addElement(
'text',
'resource_statusdat_object_file',
array(
'required' => true,
'label' => t('Filepath'),
'helptext' => t('Location of your icinga objects.cache file'),
'value' => $this->getResource()->get('status_file', '/usr/local/icinga/var/objects.cache')
)
);
}
protected function addLivestatusForm()
{
$this->addElement(
'text',
'resource_livestatus_socket',
array(
'required' => true,
'label' => t('Socket'),
'helptext' => t('The path to your livestatus socket used for querying monitoring data'),
'value' => $this->getResource()->get('socket', '/usr/local/icinga/var/rw/livestatus')
)
);
}
protected function addLdapForm()
{
$this->addElement(
'text',
'resource_ldap_hostname',
array(
'required' => true,
'allowEmpty' => false,
'label' => t('Host'),
'helptext' => t('The hostname or address of the LDAP server to use for authentication'),
'value' => $this->getResource()->get('hostname', 'localhost')
)
);
$this->addElement(
'text',
'resource_ldap_root_dn',
array(
'required' => true,
'label' => t('Root DN'),
'helptext' => t('The path where users can be found on the ldap server'),
'value' => $this->getResource()->get('root_dn', 'ou=people,dc=icinga,dc=org')
)
);
$this->addElement(
'text',
'resource_ldap_bind_dn',
array(
'required' => true,
'label' => t('Bind DN'),
'helptext' => t('The user dn to use for querying the ldap server'),
'value' => $this->getResource()->get('bind_dn', 'cn=admin,cn=config')
)
);
$this->addElement(
'password',
'resource_ldap_bind_pw',
array(
'required' => true,
'renderPassword' => true,
'label' => t('Bind Password'),
'helptext' => t('The password to use for querying the ldap server'),
'value' => $this->getResource()->get('bind_pw', '')
)
);
}
protected function addFileForm()
{
$this->addElement(
'text',
'resource_file_filename',
array(
'required' => true,
'label' => t('Filepath'),
'helptext' => t('The filename to fetch information from'),
'value' => $this->getResource()->get('filename', '')
)
);
$this->addElement(
'text',
'resource_file_fields',
array(
'required' => true,
'label' => t('Pattern'),
'helptext' => t('The regular expression by which to identify columns'),
'value' => $this->getResource()->get('fields', '')
)
);
}
protected function addNameFields()
{
$this->addElement(
'text',
'resource_all_name',
array(
'required' => true,
'label' => t('Resource Name'),
'helptext' => t('The unique name of this resource'),
'value' => $this->getName()
)
);
$this->addElement(
'hidden',
'resource_all_name_old',
array(
'value' => $this->getOldName()
)
);
}
/**
* Add checkbox at the beginning of the form which allows to skip connection validation
*/
protected function addForceCreationCheckbox()
{
$checkbox = new Zend_Form_Element_Checkbox(
array(
'order' => 0,
'name' => 'resource_force_creation',
'label' => t('Force Changes'),
'helptext' => t('Check this box to enforce changes without connectivity validation')
)
);
$checkbox->addDecorator(new HelpText());
$this->addElement($checkbox);
}
/**
* Add a select box for choosing the type to use for this backend
*/
protected function addTypeSelectionBox()
{
$this->addElement(
'select',
'resource_type',
array(
'required' => true,
'label' => t('Resource Type'),
'helptext' => t('The type of resource'),
'value' => $this->getResource()->type,
'multiOptions' => array(
'db' => t('SQL Database'),
'ldap' => 'LDAP',
'statusdat' => 'Status.dat',
'livestatus' => 'Livestatus',
'file' => t('File')
)
)
);
$this->enableAutoSubmit(array('resource_type'));
}
/**
* Validate this form with the Zend validation mechanism and perform a validation of the connection
*
* If validation fails, the 'resource_force_creation' checkbox is prepended to the form to allow users to
* skip the connection validation
*
* @param array $data The form input to validate
*
* @return bool True when validation succeeded, false if not
*/
public function isValid($data)
{
if (!parent::isValid($data)) {
return false;
}
if (isset($data['resource_force_creation']) && $data['resource_force_creation']) {
return true;
}
if (!$this->isValidResource()) {
$this->addForceCreationCheckbox();
return false;
}
return true;
}
/**
* Test if the changed resource is a valid resource, by instantiating it and
* checking if a connection is possible
*
* @return bool True when a connection to the resource is possible
*/
public function isValidResource()
{
$config = $this->getConfig();
try {
switch ($config->type) {
case 'db':
/*
* It should be possible to run icingaweb without the pgsql or mysql extension or Zend-Pdo-Classes,
* in case they aren't actually used. When the user tries to create a resource that depends on an
* uninstalled extension, an error should be displayed.
*/
if ($config->db === 'mysql' && ! Platform::extensionLoaded('mysql')) {
$this->addErrorMessage(
t('You need to install the php extension "mysql" and the ' .
'Zend_Pdo_Mysql classes to use MySQL database resources.')
);
return false;
}
if ($config->db === 'pgsql' && ! Platform::extensionLoaded('pgsql')) {
$this->addErrorMessage(
t('You need to install the php extension "pgsql" and the ' .
'Zend_Pdo_Pgsql classes to use PostgreSQL database resources.')
);
return false;
}
$resource = ResourceFactory::createResource($config);
$resource->getConnection()->getConnection();
break;
case 'statusdat':
if (!file_exists($config->object_file) || !file_exists($config->status_file)) {
$this->addErrorMessage(
t('Connectivity validation failed, the provided file does not exist.')
);
return false;
}
break;
case 'livestatus':
$resource = ResourceFactory::createResource($config);
$resource->connect()->disconnect();
break;
case 'ldap':
$resource = ResourceFactory::createResource($config);
$resource->connect();
break;
case 'file':
if (!file_exists($config->filename)) {
$this->addErrorMessage(
t('Connectivity validation failed, the provided file does not exist.')
);
return false;
}
break;
}
} catch (Exception $e) {
$this->addErrorMessage(t('Connectivity validation failed, connection to the given resource not possible.'));
return false;
}
return true;
}
public function create()
{
$this->addNameFields();
$this->addTypeSelectionBox();
switch ($this->getRequest()->getParam('resource_type', $this->getResource()->type)) {
case 'db':
$this->addDbForm();
break;
case 'statusdat':
$this->addStatusdatForm();
break;
case 'livestatus':
$this->addLivestatusForm();
break;
case 'ldap':
$this->addLdapForm();
break;
case 'file':
$this->addFileForm();
break;
}
$this->setSubmitLabel('{{SAVE_ICON}} Save Changes');
}
/**
* Return a configuration containing the backend settings entered in this form
*
* @return Zend_Config The updated configuration for this backend
*/
public function getConfig()
{
$values = $this->getValues();
$result = array('type' => $values['resource_type']);
foreach ($values as $key => $value) {
if ($key !== 'resource_type' && $key !== 'resource_all_name' && $key !== 'resource_all_name_old') {
$configKey = explode('_', $key, 3);
if (count($configKey) === 3) {
$result[$configKey[2]] = $value;
}
}
}
return new Zend_Config($result);
}
}

@ -0,0 +1,68 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form;
use Exception;
use Icinga\Web\Form;
use Icinga\Application\Config;
use Icinga\Config\PreservingIniWriter;
/**
* Form base-class providing standard functionality for configuration forms
*/
class ConfigForm extends Form
{
/**
* The configuration to work with
*
* @var Config
*/
protected $config;
/**
* Set the configuration to use when populating the form or when saving the user's input
*
* @param Config $config The configuration to use
*
* @return self
*/
public function setIniConfig(Config $config)
{
$this->config = $config;
return $this;
}
/**
* Persist the current configuration to disk
*
* If an error occurs the user is shown a view describing the issue and displaying the raw INI configuration.
*
* @return bool Whether the configuration could be persisted
*/
public function save()
{
$writer = new PreservingIniWriter(
array(
'config' => $this->config,
'filename' => $this->config->getConfigFile()
)
);
try {
$writer->write();
} catch (Exception $e) {
$this->addDecorator('ViewScript', array(
'viewModule' => 'default',
'viewScript' => 'showConfiguration.phtml',
'errorMessage' => $e->getMessage(),
'configString' => $writer->render(),
'filePath' => $this->config->getConfigFile()
));
return false;
}
return true;
}
}

@ -0,0 +1,22 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form;
use Icinga\Web\Form;
/**
* Form for confirming removal of an object
*/
class ConfirmRemovalForm extends Form
{
/**
* Initalize this form
*/
public function init()
{
$this->setName('form_confirm_removal');
$this->setSubmitLabel(t('Confirm Removal'));
}
}

@ -5,12 +5,8 @@
namespace Icinga\Form\Dashboard;
use Icinga\Application\Config as IcingaConfig;
use Icinga\Web\Form;
use Icinga\Web\Widget\Dashboard;
use Zend_Form_Element_Text;
use Zend_Form_Element_Submit;
use Zend_Form_Element_Hidden;
use Zend_Form_Element_Select;
use Icinga\Web\Form;
/**
* Form to add an url a dashboard pane
@ -18,124 +14,103 @@ use Zend_Form_Element_Select;
class AddUrlForm extends Form
{
/**
* Add a selection box for different panes to the form
*
* @param Dashboard $dashboard The dashboard to retrieve the panes from
* Initialize this form
*/
private function addPaneSelectionBox(Dashboard $dashboard)
public function init()
{
$selectPane = new Zend_Form_Element_Select(
'pane',
array(
'label' => 'Dashboard',
'required' => true,
'style' => 'display:inline-block;',
'multiOptions' => $dashboard->getPaneKeyTitleArray()
)
);
$newDashboardBtn = new Zend_Form_Element_Submit(
'create_new_pane',
array(
'label' => 'Create A New Pane',
'required' => false,
'class' => 'btn btn-default',
'style' => 'display:inline-block'
)
);
$newDashboardBtn->removeDecorator('DtDdWrapper');
$selectPane->removeDecorator('DtDdWrapper');
$selectPane->removeDecorator('htmlTag');
$this->addElement($selectPane);
$this->addElement($newDashboardBtn);
$this->enableAutoSubmit(array('create_new_pane'));
$this->setName('form_dashboard_addurl');
$this->setSubmitLabel(t('Add To Dashboard'));
}
/**
* Add a textfield for creating a new pane to this form
* @see Form::createElements()
*/
private function addNewPaneTextField($showExistingButton = true)
public function createElements(array $formData)
{
$txtCreatePane = new Zend_Form_Element_Text(
'pane',
array(
'label' => 'New Dashboard Title',
'required' => true,
'style' => 'display:inline-block'
)
);
// Marks this field as a new pane (and prevents the checkbox being displayed when validation errors occur)
$markAsNewPane = new Zend_Form_Element_Hidden(
'create_new_pane',
array(
'required' => true,
'value' => 1
)
);
$cancelDashboardBtn = new Zend_Form_Element_Submit(
'use_existing_dashboard',
array(
'class' => 'btn',
'escape' => false,
'label' => 'Use An Existing Dashboard',
'required' => false
)
);
$cancelDashboardBtn->removeDecorator('DtDdWrapper');
$txtCreatePane->removeDecorator('DtDdWrapper');
$txtCreatePane->removeDecorator('htmlTag');
$this->addElement($txtCreatePane);
if ($showExistingButton) {
$this->addElement($cancelDashboardBtn);
}
$this->addElement($markAsNewPane);
}
/**
* Add elements to this form (used by extending classes)
*/
protected function create()
{
$dashboard = new Dashboard();
$this->setName('form_dashboard_add');
$dashboard->readConfig(IcingaConfig::app('dashboard/dashboard'));
$this->addElement(
'text',
'url',
array(
'label' => 'Url',
'required' => true,
'value' => htmlspecialchars_decode($this->getRequest()->getParam('url', ''))
'required' => true,
'label' => t('Url'),
'helptext' => t('The url being loaded in the dashlet')
)
);
$elems = $dashboard->getPaneKeyTitleArray();
if (empty($elems) || // show textfield instead of combobox when no pane is available
($this->getRequest()->getPost('create_new_pane', '0') && // or when a new pane should be created (+ button)
!$this->getRequest()->getPost('use_existing_dashboard', '0')) // and the user didn't click the 'use
// existing' button
$paneSelectionValues = $this->getDashboardPaneSelectionValues();
if (empty($paneSelectionValues) ||
((isset($formData['create_new_pane']) && $formData['create_new_pane'] != false) &&
(false === isset($formData['use_existing_dashboard']) || $formData['use_existing_dashboard'] != true))
) {
$this->addNewPaneTextField(!empty($elems));
$this->addElement(
'text',
'pane',
array(
'required' => true,
'label' => t("The New Pane's Title"),
'style' => 'display: inline-block'
)
);
$this->addElement( // Prevent the button from being displayed again on validation errors
'hidden',
'create_new_pane',
array(
'value' => 1
)
);
if (false === empty($paneSelectionValues)) {
$this->addElement(
'submit',
'use_existing_dashboard',
array(
'ignore' => true,
'label' => t('Use An Existing Pane'),
'style' => 'display: inline-block'
)
);
}
} else {
$this->addPaneSelectionBox($dashboard);
$this->addElement(
'select',
'pane',
array(
'required' => true,
'label' => t('Pane'),
'style' => 'display: inline-block;',
'multiOptions' => $paneSelectionValues
)
);
$this->addElement(
'submit',
'create_new_pane',
array(
'ignore' => true,
'label' => t('Create A New Pane'),
'style' => 'display: inline-block'
)
);
}
$this->addElement(
'text',
'component',
array(
'label' => 'Title',
'required' => true,
'required' => true,
'label' => t('Title'),
'helptext' => t('The title for the dashlet')
)
);
$this->setSubmitLabel("Add To Dashboard");
}
/**
* Return the names and titles of the available dashboard panes as key-value array
*
* @return array
*/
protected function getDashboardPaneSelectionValues()
{
$dashboard = new Dashboard();
$dashboard->readConfig(IcingaConfig::app('dashboard/dashboard'));
return $dashboard->getPaneKeyTitleArray();
}
}

@ -1,65 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Install;
use Zend_Config;
use Icinga\Web\Wizard\Page;
use Icinga\Form\Config\LoggingForm;
class LoggingPage extends Page
{
/**
* The logging form
*
* @var LoggingForm
*/
protected $loggingForm;
/**
* Initialize this LoggingPage
*/
public function init()
{
$this->setName('logging');
}
/**
* Create and return the logging form
*
* @return LoggingForm
*/
protected function createForm()
{
if ($this->loggingForm === null) {
$this->loggingForm = new LoggingForm();
$this->loggingForm->hideButtons();
$this->loggingForm->setTokenDisabled();
$this->loggingForm->setRequest($this->getRequest());
$this->loggingForm->setConfiguration($this->getConfiguration());
}
return $this->loggingForm;
}
/**
* Create this wizard page
*/
protected function create()
{
$loggingForm = $this->createForm();
$loggingForm->buildForm(); // Needs to get called manually as it's nothing that Zend knows about
$this->addSubForm($loggingForm, $loggingForm->getName());
}
/**
* Return a config containing all values provided by the user
*
* @return Zend_Config
*/
public function getConfig()
{
return $this->createForm()->getConfig();
}
}

@ -1,163 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form\Preference;
use DateTimeZone;
use Icinga\Util\TimezoneDetect;
use Zend_Config;
use Zend_Form_Element_Text;
use Zend_Form_Element_Select;
use Zend_View_Helper_DateFormat;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\TimeFormatValidator;
use Icinga\Web\Form\Validator\DateFormatValidator;
use Icinga\Util\Translator;
/**
* General user preferences
*/
class GeneralForm extends Form
{
/**
* Add a select field for setting the user's language
*
* Possible values are determined by Translator::getAvailableLocaleCodes.
* Also, a 'use default format' checkbox is added in order to allow a user to discard his overwritten setting
*
* @param Zend_Config $cfg The "global" section of the config.ini to be used as default value
*/
private function addLanguageSelection(Zend_Config $cfg)
{
$languages = array();
foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language;
}
$prefs = $this->getUserPreferences();
$useDefaultLanguage = $this->getRequest()->getParam('default_language', !$prefs->has('app.language'));
$this->addElement(
'checkbox',
'default_language',
array(
'label' => t('Use Default Language'),
'value' => $useDefaultLanguage,
'required' => true
)
);
$selectOptions = array(
'label' => t('Your Current Language'),
'required' => !$useDefaultLanguage,
'multiOptions' => $languages,
'helptext' => t('Use the following language to display texts and messages'),
'value' => $prefs->get('app.language', $cfg->get('language', Translator::DEFAULT_LOCALE))
);
if ($useDefaultLanguage) {
$selectOptions['disabled'] = 'disabled';
}
$this->addElement('select', 'language', $selectOptions);
$this->enableAutoSubmit(array('default_language'));
}
/**
* Add a select field for setting the user's timezone.
*
* Possible values are determined by DateTimeZone::listIdentifiers
* Also, a 'use default format' checkbox is added in order to allow a user to discard his overwritten setting
*
* @param Zend_Config $cfg The "global" section of the config.ini to be used as default value
*/
private function addTimezoneSelection(Zend_Config $cfg)
{
$prefs = $this->getUserPreferences();
$useGlobalTimezone = $this->getRequest()->getParam('default_timezone', !$prefs->has('app.timezone'));
$detect = new TimezoneDetect();
$tzList = array();
foreach (DateTimeZone::listIdentifiers() as $tz) {
$tzList[$tz] = $tz;
}
$helptext = 'Use the following timezone for dates and times';
if ($useGlobalTimezone && $detect->success() === true) {
$helptext .= '<br />' . t('Currently your time was detected to be')
. ': '
. '<strong>' . $detect->getTimezoneName() . '</strong>';
}
$selectTimezone = new Zend_Form_Element_Select(
array(
'name' => 'timezone',
'label' => 'Your Current Timezone',
'required' => !$useGlobalTimezone,
'multiOptions' => $tzList,
'helptext' => $helptext,
'value' => $prefs->get('app.timezone', $cfg->get('timezone', date_default_timezone_get()))
)
);
$this->addElement(
'checkbox',
'default_timezone',
array(
'label' => 'Use Default Timezone',
'value' => $useGlobalTimezone,
'required' => true
)
);
if ($useGlobalTimezone) {
$selectTimezone->setAttrib('disabled', 1);
}
$this->addElement($selectTimezone);
$this->enableAutoSubmit(array('default_timezone'));
}
/**
* Create the general form, using the global configuration as fallback values for preferences
*
* @see Form::create()
*/
public function create()
{
$this->setName('form_preference_set');
$config = $this->getConfiguration();
$global = $config->global;
if ($global === null) {
$global = new Zend_Config(array());
}
$this->addLanguageSelection($global);
$this->addTimezoneSelection($global);
$this->setSubmitLabel('Save Changes');
$this->addElement(
'checkbox',
'show_benchmark',
array(
'label' => 'Use benchmark',
'value' => $this->getUserPreferences()->get('app.show_benchmark')
)
);
}
/**
* Return an array containing the preferences set in this form
*
* @return array
*/
public function getPreferences()
{
$values = $this->getValues();
return array(
'app.language' => $values['default_language'] ? null : $values['language'],
'app.timezone' => $values['default_timezone'] ? null : $values['timezone'],
'app.show_benchmark' => $values['show_benchmark'] === '1' ? true : false
);
}
}

@ -0,0 +1,225 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form;
use Exception;
use DateTimeZone;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Web\Session;
use Icinga\Web\Notification;
use Icinga\Util\Translator;
use Icinga\Util\TimezoneDetect;
use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore;
/**
* Form class to adjust user preferences
*/
class PreferenceForm extends Form
{
/**
* The preferences to work with
*
* @var Preferences
*/
protected $preferences;
/**
* The preference store to use
*
* @var PreferencesStore
*/
protected $store;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_preferences');
$this->setSubmitLabel(t('Save Changes'));
}
/**
* Set preferences to work with
*
* @param Preferences $preferences The preferences to work with
*
* @return self
*/
public function setPreferences(Preferences $preferences)
{
$this->preferences = $preferences;
return $this;
}
/**
* Set the preference store to use
*
* @param PreferencesStore $store The preference store to use
*
* @return self
*/
public function setStore(PreferencesStore $store)
{
$this->store = $store;
}
/**
* Persist preferences
*
* @return self
*/
public function save()
{
$this->store->load(); // Necessary for patching existing preferences
$this->store->save($this->preferences);
return $this;
}
/**
* Adjust preferences and persist them
*
* @see Form::onSuccess()
*/
public function onSuccess(Request $request)
{
$webPreferences = $this->preferences->get('icingaweb', array());
foreach ($this->getValues() as $key => $value) {
if ($value === null) {
if (isset($webPreferences[$key])) {
unset($webPreferences[$key]);
}
} else {
$webPreferences[$key] = $value;
}
}
$this->preferences->icingaweb = $webPreferences;
// TODO: Is this even necessary in case the session is written on response?
$session = Session::getSession();
$session->user->setPreferences($this->preferences);
$session->write();
try {
$this->save();
Notification::success(t('Preferences successfully saved'));
} catch (Exception $e) {
Notification::error($e->getMessage());
}
}
/**
* Populate preferences
*
* @see Form::onRequest()
*/
public function onRequest(Request $request)
{
$values = $this->preferences->get('icingaweb', array());
$values['browser_language'] = false === isset($values['language']);
$values['local_timezone'] = false === isset($values['timezone']);
$this->populate($values);
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$languages = array();
foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language;
}
$tzList = array();
foreach (DateTimeZone::listIdentifiers() as $tz) {
$tzList[$tz] = $tz;
}
$this->addElement(
'checkbox',
'browser_language',
array(
'ignore' => true,
'required' => true,
'autosubmit' => true,
'value' => true,
'label' => t('Use your browser\'s language suggestions')
)
);
$useBrowserLanguage = isset($formData['browser_language']) ? $formData['browser_language'] == 1 : true;
$languageSelection = $this->createElement(
'select',
'language',
array(
'required' => false === $useBrowserLanguage,
'label' => t('Your Current Language'),
'description' => t('Use the following language to display texts and messages'),
'multiOptions' => $languages,
'value' => substr(setlocale(LC_ALL, 0), 0, 5)
)
);
if ($useBrowserLanguage) {
$languageSelection->setAttrib('disabled', 'disabled');
}
$this->addElement($languageSelection);
$this->addElement(
'checkbox',
'local_timezone',
array(
'ignore' => true,
'required' => true,
'autosubmit' => true,
'value' => true,
'label' => t('Use your local timezone')
)
);
$useLocalTimezone = isset($formData['local_timezone']) ? $formData['local_timezone'] == 1 : true;
$timezoneSelection = $this->createElement(
'select',
'timezone',
array(
'required' => false === $useLocalTimezone,
'label' => t('Your Current Timezone'),
'description' => t('Use the following timezone for dates and times'),
'multiOptions' => $tzList,
'value' => $this->getDefaultTimezone()
)
);
if ($useLocalTimezone) {
$timezoneSelection->setAttrib('disabled', 'disabled');
}
$this->addElement($timezoneSelection);
$this->addElement(
'checkbox',
'show_benchmark',
array(
'required' => true,
'label' => t('Use benchmark')
)
);
}
/**
* Return the current default timezone
*
* @return string
*/
protected function getDefaultTimezone()
{
$detect = new TimezoneDetect();
if ($detect->success()) {
return $detect->getTimezoneName();
} else {
return date_default_timezone_get();
}
}
}

@ -486,7 +486,7 @@ msgstr "Die Objekt-Klasse welche benutzt wird, um Benutzer auf diesem LDAP-Serve
#: /usr/local/src/bugfix.master/application/forms/Config/ResourceForm.php:182
msgid "The password to use for authentication"
msgstr "Das Kennwort welche zur Authentifizierung benutzt werden soll"
msgstr "Das Kennwort welches zur Authentifizierung benutzt werden soll"
#: /usr/local/src/bugfix.master/application/forms/Config/ResourceForm.php:270
msgid "The password to use for querying the ldap server"

@ -4,7 +4,6 @@
use Icinga\Application\Icinga;
use Icinga\Util\DateTimeFactory;
use Icinga\Web\Form\Validator\DateTimeValidator;
/**
* Helper to format date and time. Utilizes DateTimeFactory to ensure time zone awareness
@ -57,7 +56,7 @@ class Zend_View_Helper_DateFormat extends Zend_View_Helper_Abstract
public function format($timestamp, $format)
{
$dt = DateTimeFactory::create();
if (DateTimeValidator::isUnixTimestamp($timestamp)) {
if (DateTimeFactory::isUnixTimestamp($timestamp)) {
$dt->setTimestamp($timestamp);
} else {
return $timestamp;

@ -2,18 +2,30 @@
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
use \Zend_View_Helper_FormElement;
/**
* Helper to generate a "datetime" element
* Render date-and-time input controls
*/
class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
{
/**
* Generate a 'datetime' element
* Format date and time
*
* @param DateTime $dateTime
* @param bool $local
*
* @return string
*/
public function formatDate(DateTime $dateTime, $local)
{
$format = (bool) $local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339;
return $dateTime->format($format);
}
/**
* Render the date-and-time input control
*
* @param string $name The element name
* @param int $value The default timestamp
* @param DateTime $value The default timestamp
* @param array $attribs Attributes for the element tag
*
* @return string The element XHTML
@ -21,50 +33,41 @@ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
public function formDateTime($name, $value = null, $attribs = null)
{
$info = $this->_getInfo($name, $value, $attribs);
extract($info); // name, value, attribs, options, listsep, disable
// Is it disabled?
extract($info); // name, id, value, attribs, options, listsep, disable
/** @var string $id */
/** @var bool $disable */
$disabled = '';
if ($disabled) {
if ($disable) {
$disabled = ' disabled="disabled"';
}
$jspicker = (isset($attribs['jspicker']) && $attribs['jspicker'] === true) ? true : false;
if (isset($value) && !empty($value)) {
if ($jspicker) {
$value = ' value="' . $this->view->dateFormat()->format($value, $attribs['defaultFormat']) . '"';
} else {
$value = ' value="' . $this->view->dateFormat()->formatDateTime($value) . '"';
}
} else {
$value = '';
if ($value instanceof DateTime) {
// If value was valid, it's a DateTime object
$value = $this->formatDate($value, $attribs['local']);
}
// Build the element
$xhtml = '<div class="datetime' . (($jspicker === true) ? ' input-group' : ''). '">';
$xhtml .= '<input type="text" name="' . $name . '"'
. ' id="' . $name . '"'
. $value
. $disabled
. $this->_htmlAttribs($attribs);
if ($jspicker === true) {
$xhtml .= 'data-icinga-component="app/datetime"';
$min = '';
if (! empty($attribs['min'])) {
$min = sprintf(' min="%s"', $this->formatDate($attribs['min'], $attribs['local']));
}
$xhtml .= $this->getClosingBracket();
if ($jspicker === true) {
$xhtml .= '<span class="input-group-addon">'
. '<a href="#">'
. '<i class="icinga-icon-reschedule"></i>'
. '</a>'
. '</span>';
unset($attribs['min']); // Unset min to not render it again in $this->_htmlAttribs($attribs)
$max = '';
if (! empty($attribs['max'])) {
$max = sprintf(' max="%s"', $this->formatDate($attribs['max'], $attribs['local']));
}
$xhtml .= '</div>';
return $xhtml;
unset($attribs['max']); // Unset max to not render it again in $this->_htmlAttribs($attribs)
$type = $attribs['local'] === true ? 'datetime-local' : 'datetime';
unset($attribs['local']); // Unset local to not render it again in $this->_htmlAttribs($attribs)
$html5 = sprintf(
'<input type="%s" name="%s" id="%s" value="%s"%s%s%s%s%s',
$type,
$this->view->escape($name),
$this->view->escape($id),
$this->view->escape($value),
$min,
$max,
$disabled,
$this->_htmlAttribs($attribs),
$this->getClosingBracket()
);
return $html5;
}
}

@ -2,29 +2,79 @@
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
use \Zend_View_Helper_FormElement;
/**
* Helper to generate a number input
* Render number input controls
*/
class Zend_View_Helper_FormNumber extends \Zend_View_Helper_FormText
class Zend_View_Helper_FormNumber extends Zend_View_Helper_FormElement
{
/**
* Generates a html number input
* Format a number
*
* @access public
* @param $number
*
* @param string $name The element name.
* @param string $value The default value.
* @param array $attribs Attributes which should be added to the input tag.
* @return string
*/
public function formatNumber($number)
{
if (empty($number)) {
return $number;
}
return $this->view->escape(
sprintf(
ctype_digit((string) $number) ? '%d' : '%F',
$number
)
);
}
/**
* Render the number input control
*
* @return string The input tag and options XHTML.
* @param string $name
* @param int $value
* @param array $attribs
*
* @return string The rendered number input control
*/
public function formNumber($name, $value = null, $attribs = null)
{
return '<input type="number"'
. ' name="' . $this->view->escape($name) . '"'
. ' value="' . $this->view->escape($value) . '"'
. ' id="' . $this->view->escape($name) . '"'
. $this->_htmlAttribs($attribs)
. $this->getClosingBracket();
$info = $this->_getInfo($name, $value, $attribs);
extract($info); // name, id, value, attribs, options, listsep, disable
/** @var string $id */
/** @var bool $disable */
$disabled = '';
if ($disable) {
$disabled = ' disabled="disabled"';
}
$min = '';
if (isset($attribs['min'])) {
$min = sprintf(' min="%s"', $this->formatNumber($attribs['min']));
}
unset($attribs['min']); // Unset min to not render it again in $this->_htmlAttribs($attribs)
$max = '';
if (isset($attribs['max'])) {
$max = sprintf(' max="%s"', $this->formatNumber($attribs['max']));
}
unset($attribs['max']); // Unset max to not render it again in $this->_htmlAttribs($attribs)
$step = '';
if (isset($attribs['step'])) {
$step = sprintf(' step="%s"', $attribs['step'] === 'any' ? 'any' : $this->formatNumber($attribs['step']));
}
unset($attribs['step']); // Unset step to not render it again in $this->_htmlAttribs($attribs)
$html5 = sprintf(
'<input type="number" name="%s" id="%s" value="%s"%s%s%s%s%s%s',
$this->view->escape($name),
$this->view->escape($id),
$this->view->escape($this->formatNumber($value)),
$min,
$max,
$step,
$disabled,
$this->_htmlAttribs($attribs),
$this->getClosingBracket()
);
return $html5;
}
}

@ -64,6 +64,9 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract
return date('H:i d.m.Y', $timestamp);
}
/**
* @deprecated Not used. This is monitoring module stuff.
*/
public static function getHostStateClassName($state)
{
$class = 'unknown';
@ -82,6 +85,9 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract
return $class;
}
/**
* @deprecated Crap. This is monitoring module stuff.
*/
public static function getHostStateName($state)
{
$states = array(
@ -98,6 +104,9 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract
return sprintf('OUT OF BOUNDS (%s)', var_export($state, 1));
}
/**
* @deprecated Crap. This is monitoring module stuff.
*/
public static function getServiceStateName($state)
{
if ($state === null) { $state = 3; } // really?

@ -1,45 +0,0 @@
<div class="controls">
<?= $this->tabs ?>
</div>
<div class="content" data-base-target="_next">
<?php
if (isset($this->messageBox)) {
// TODO: Get rid of such boxes -> notifications?
echo $this->messageBox->render();
}
?>
<p>
<a href="<?= $this->href('/config/createAuthenticationBackend', array('type' => 'ldap')) ?>"><?= $this->icon('create.png'); ?> Create A New LDAP Authentication Backend</a>
<br />
<a href="<?= $this->href('/config/createAuthenticationBackend', array('type' => 'db')) ?>"><?= $this->icon('create.png'); ?> Create A New DB Authentication Backend</a>
</p>
<table class="action">
<thead>
<th>Resource</th>
<th style="width: 5em">Remove</th>
<th style="width: 5em">Order</th>
</thead>
<tbody>
<?php foreach ($this->backends as $backend): ?>
<tr>
<td class="action">
<a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backend->name)) ?>">
<?= $this->icon('edit.png') ?> <?= $this->escape($backend->name); ?>
</a>
</td>
<td>
<a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backend->name)) ?>">
<?= $this->icon('remove.png', 'Remove') ?>
</a>
</td>
<td>
<?= $backend->reorderForm; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>

@ -1,15 +1,10 @@
<h4>
<i class="icinga-icon-create"></i>
Create New Authentication Backend
</h4>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Create New Authentication Backend'); ?></h4>
<p>
Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.
<?= $this->translate(
'Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.'
); ?>
</p>
<?= $this->form ?>
<?= $form; ?>

@ -1,18 +1,5 @@
<h4>
<i class="icinga-icon-edit"></i>
Edit Backend "<?= $this->escape($this->name); ?>"
</h4>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<?php if ($this->form->getErrorMessages()): ?>
<div>
<?php foreach ($this->form->getErrorMessages() as $error): ?>
<?= $this->escape($error); ?><br/>
<?php endforeach; ?>
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<?php endif; ?>
<?= $this->form ?>
<h4><?= $this->translate('Edit Backend'); ?></h4>
<?= $form; ?>

@ -1,10 +1,5 @@
<h4>
<i class="icinga-icon-remove"></i>
Remove Backend "<?= $this->escape($this->name); ?>"
</h4>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<?= $this->form ?>
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Remove Backend'); ?></h4>
<?= $form; ?>

@ -0,0 +1,11 @@
<div class="controls">
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<p>
<a href="<?= $this->href('/config/createAuthenticationBackend'); ?>">
<?= $this->icon('create.png'); ?><?= $this->translate('Create A New Authentication Backend'); ?>
</a>
</p>
<?= $form; ?>
</div>

@ -1,6 +1,6 @@
<div class="controls">
<?= $this->tabs ?>
<h1>Installed Modules</h1>
<h1><?= $this->translate('Installed Modules') ?></h1>
<?= $this->paginationControl($modules) ?>
</div>
@ -11,9 +11,9 @@
<tr>
<td>
<?php if ($module->enabled): ?>
<?= $this->icon('success.png', 'Module is enabled') ?>
<?= $this->icon('success.png', $this->translate('Module is enabled')) ?>
<?php else: ?>
<?= $this->icon('remove.png', 'Module is disabled') ?>
<?= $this->icon('remove.png', $this->translate('Module is disabled')) ?>
<? endif ?>
<a href="<?= $this->url(
'config/module/',

@ -1,30 +1,32 @@
<div class="controls">
<?= $this->tabs ?>
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<?php
if (isset($this->messageBox)) {
// TODO: Get rid of messageBoxes in favour of notifications
echo $this->messageBox->render();
}
?>
<p><a href="<?= $this->href('/config/createresource') ?>"><?= $this->icon('create.png'); ?> Create A New Resource</a></p>
<table class="action">
<thead>
<th>Resource</th>
<th style="width: 5em">Remove</th>
</thead>
<tbody>
<p>
<a href="<?= $this->href('/config/createresource'); ?>">
<?= $this->icon('create.png'); ?> <?= $this->translate('Create A New Resource'); ?>
</a>
</p>
<table class="action">
<thead>
<th><?= $this->translate('Resource'); ?></th>
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
</thead>
<tbody>
<?php foreach ($this->resources as $name => $resource): ?>
<tr>
<td><a href="<?= $this->href('config/editresource', array('resource' => $name)) ?>"><?= $this->icon('edit.png') ?> <?= $this->escape($name); ?></td>
<td style="text-align: center"><a href="<?= $this->href('config/removeresource', array('resource' => $name)) ?>"><?= $this->icon('remove.png'); ?></a></td>
</tr>
<tr>
<td>
<a href="<?= $this->href('config/editresource', array('resource' => $name)); ?>">
<?= $this->icon('edit.png'); ?> <?= $this->escape($name); ?>
</a>
</td>
<td style="text-align: center">
<a href="<?= $this->href('config/removeresource', array('resource' => $name)); ?>">
<?= $this->icon('remove.png'); ?>
</a>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
</tbody>
</table>
</div>

@ -1,14 +1,6 @@
<h4>
<i class="icinga-icon-create"></i>
Create New Resource
</h4>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<p>
Resources are entities that provide data to Icingaweb.
</p>
<?= $this->form ?>
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Create A New Resource'); ?></h4>
<p><?= $this->translate('Resources are entities that provide data to Icinga Web 2.'); ?></p>
<?= $form; ?>

@ -1,18 +1,5 @@
<h4>
<i class="icinga-icon-edit"></i>
Edit Resource "<?= $this->escape($this->name); ?>"
</h4>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<?php if ($this->form->getErrorMessages()): ?>
<div>
<?php foreach ($this->form->getErrorMessages() as $error): ?>
<?= $this->escape($error); ?><br/>
<?php endforeach; ?>
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<?php endif ?>
<?= $this->form ?>
<h4><?= $this->translate('Edit Existing Resource'); ?></h4>
<?= $form; ?>

@ -1,10 +1,5 @@
<h4>
<i class="icinga-icon-remove"></i>
Remove Resource "<?= $this->escape($this->name); ?>"
</h4>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<?= $this->form ?>
<div class="controls">
<?= $this->tabs->showOnlyCloseButton() ?>
</div>
<h4><?= $this->translate('Remove Existing Resource'); ?></h4>
<?= $form; ?>

@ -1,31 +0,0 @@
<?= $this->tabs->render($this); ?>
<br/>
<div>
<h4><i>WARNING ICON</i>Saving "<?= $this->escape($this->file); ?>.ini" Failed</h4>
<br/>
<p>
Your <?= $this->escape($this->file); ?> configuration couldn't be stored (error: "<?= $this->exceptionMessage; ?>").<br/>
This could have one or more of the following reasons:
</p>
<ul>
<li>You don't have file-system permissions to write to the <?= $this->escape($this->file); ?>.ini file</li>
<li>Something went wrong while writing the file</li>
<li>There's an application error preventing you from persisting the configuration</li>
</ul>
</div>
<p>
Details can be seen in your application log (if you don't have access to this file, call your administrator in this case).
<br/>
In case you can access the configuration file (config/<?= $this->escape($this->file); ?>.ini) by yourself, you can open it and
insert the config manually:
</p>
<p>
<pre>
<code>
<?= $this->escape($this->iniConfigurationString); ?>
</code>
</pre>
</p>

@ -11,7 +11,4 @@
<hr />
<pre><?= $this->escape($stackTrace) ?></pre>
<?php endif ?>
<?php if (isset($this->messageBox)) : ?>
<?= $this->messageBox->render(); ?>
<? endif ?>
</div>

@ -0,0 +1,40 @@
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>">
<table class="action">
<thead>
<th>Backend</th>
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
<th style="width: 5em"><?= $this->translate('Order'); ?></th>
</thead>
<tbody>
<?php $backendNames = $form->getBackendOrder(); ?>
<?php for ($i = 0; $i < count($backendNames); $i++): ?>
<tr>
<td class="action">
<a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backendNames[$i])); ?>">
<?= $this->icon('edit.png'); ?> <?= $this->escape($backendNames[$i]); ?>
</a>
</td>
<td>
<a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backendNames[$i])); ?>">
<?= $this->icon('remove.png', $this->translate('Remove')); ?>
</a>
</td>
<td>
<?php if ($i > 0): ?>
<button type="submit" name="backend_newpos" value="<?= sprintf('%s|%s', $backendNames[$i], $i - 1); ?>">
<?= $this->icon('up.png', $this->translate('Move up in authentication order')); ?>
</button>
<?php endif; ?>
<?php if ($i + 1 < count($backendNames)): ?>
<button type="submit" name="backend_newpos" value="<?= sprintf('%s|%s', $backendNames[$i], $i + 1); ?>">
<?= $this->icon('down.png', $this->translate('Move down in authentication order')); ?>
</button>
<?php endif; ?>
</td>
</tr>
<?php endfor; ?>
</tbody>
</table>
<?= $form->getElement($form->getTokenElementName()); ?>
<?= $form->getElement($form->getUidElementName()); ?>
</form>

@ -1,7 +1,7 @@
<div class="controls">
<?= $this->tabs->render($this); ?>
<?= $tabs; ?>
</div>
<div class="content">
<?= $this->form ?>
</div>
<?= $form; ?>
</div>

@ -0,0 +1,28 @@
<div>
<h4><?= $this->translate('Saving Configuration Failed'); ?></h4>
<br>
<p>
<?= sprintf(
$this->translate('The file %s couldn\'t be stored. (Error: "%s")'),
$this->escape($filePath),
$this->escape($errorMessage)
); ?>
<br>
<?= $this->translate('This could have one or more of the following reasons:'); ?>
</p>
<ul>
<li><?= $this->translate('You don\'t have file-system permissions to write to the file'); ?></li>
<li><?= $this->translate('Something went wrong while writing the file'); ?></li>
<li><?= $this->translate('There\'s an application error preventing you from persisting the configuration'); ?></li>
</ul>
</div>
<p>
<?= $this->translate('Details can be found in the application log. (If you don\'t have access to this log, call your administrator in this case)'); ?>
<br>
<?= $this->translate('In case you can access the file by yourself, you can open it and insert the config manually:'); ?>
</p>
<p>
<pre>
<code><?= $this->escape($configString); ?></code>
</pre>
</p>

@ -18,6 +18,10 @@ backend = ldap
resource = internal_ldap
user_class = @ldap_user_objectclass@
user_name_attribute = @ldap_attribute_username@
group_base_dn = @ldap_group_base_dn@
group_attribute = @ldap_group_attribute@
group_member_attribute = @ldap_group_member_attribute@
group_class = @ldap_group_class@
[internal_db_authentication]
@internal_auth_disabled@

@ -26,12 +26,12 @@
%define revision 1
%define configdir %{_sysconfdir}/icingaweb
%define sharedir %{_datadir}/icingaweb
%define prefixdir %{_datadir}/icingaweb
%define logdir %{sharedir}/log
%define configdir %{_sysconfdir}/%{name}
%define sharedir %{_datadir}/%{name}
%define prefixdir %{_datadir}/%{name}
%define usermodparam -a -G
%define logdir %{_localstatedir}/log/icingaweb
%define logdir %{_localstatedir}/log/%{name}
%define docdir %{sharedir}/log
%if "%{_vendor}" == "suse"
%define phpname php5
@ -172,25 +172,26 @@ install -D -m0644 packages/rpm/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{ap
# install public, library, modules
%{__mkdir} -p %{buildroot}/%{sharedir}
%{__mkdir} -p %{buildroot}/%{logdir}
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb
%{__mkdir} -p %{buildroot}/%{docdir}
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}
%{__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}/%{name}/modules
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/enabledModules
%{__cp} -r application library modules public %{buildroot}/%{sharedir}/
%{__cp} -r application doc library modules public %{buildroot}/%{sharedir}/
## config
# authentication is db only
install -D -m0644 packages/rpm/etc/icingaweb/authentication.ini %{buildroot}/%{_sysconfdir}/icingaweb/authentication.ini
install -D -m0644 packages/rpm/etc/%{name}/authentication.ini %{buildroot}/%{_sysconfdir}/%{name}/authentication.ini
# custom resource paths
install -D -m0644 packages/rpm/etc/icingaweb/resources.ini %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini
install -D -m0644 packages/rpm/etc/%{name}/resources.ini %{buildroot}/%{_sysconfdir}/%{name}/resources.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
install -D -m0644 packages/rpm/etc/%{name}/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring/backends.ini
install -D -m0644 packages/rpm/etc/%{name}/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring/instances.ini
# enable the monitoring module by default
ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring
ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/%{name}/enabledModules/monitoring
## config
# install icingacli
@ -228,6 +229,8 @@ fi
%config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir}
# logs
%attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir}
# shipped docs
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/doc
%files -n php-Icinga
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/application

@ -4,10 +4,10 @@
namespace Icinga\Application;
use ErrorException;
use Exception;
use Zend_Config;
use Icinga\Application\Modules\Manager as ModuleManager;
use Icinga\Application\Config;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
@ -348,11 +348,7 @@ abstract class ApplicationBootstrap
Logger::create(
new Zend_Config(
array(
'enable' => true,
'level' => Logger::$ERROR,
'type' => 'syslog',
'facility' => 'LOG_USER',
'application' => 'icingaweb'
'log' => 'syslog'
)
)
);
@ -383,9 +379,20 @@ abstract class ApplicationBootstrap
*/
protected function setupErrorHandling()
{
error_reporting(E_ALL | E_NOTICE);
error_reporting(E_ALL | E_STRICT);
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
if (error_reporting() === 0) {
// Error was suppressed with the @-operator
return false; // Continue with the normal error handler
}
switch($errno) {
case E_STRICT:
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
return false; // Continue with the normal error handler
});
return $this;
}

@ -51,10 +51,9 @@ class Cli extends ApplicationBootstrap
Logger::create(
new Zend_Config(
array(
'enable' => true,
'level' => Logger::$INFO,
'type' => 'file',
'target' => 'php://stderr'
'level' => Logger::INFO,
'log' => 'file',
'file' => 'php://stderr'
)
)
);

@ -7,7 +7,6 @@ namespace Icinga\Application;
use Zend_Config;
use Zend_Config_Ini;
use Icinga\Exception\NotReadableError;
use Icinga\Exception\ProgrammingError;
/**
* Global registry of application and module configuration.
@ -22,11 +21,11 @@ class Config extends Zend_Config
public static $configDir;
/**
* The INI file this configuration has been loaded from
* The INI file this configuration has been loaded from or should be written to
*
* @var string
*/
private $configFile;
protected $configFile;
/**
* Application config instances per file
@ -42,30 +41,28 @@ class Config extends Zend_Config
*/
protected static $modules = array();
private $instance;
/**
* Load configuration from the config file $filename
* Load configuration from the given INI file
*
* @param string $file The file to parse
*
* @param string $filename The filename to parse
* @throws NotReadableError When the file does not exist or cannot be read
*/
public function __construct($filename)
public static function fromIni($file)
{
parent::__construct(array(), true);
$filepath = realpath($filename);
$config = new static(array(), true);
$filepath = realpath($file);
if ($filepath === false) {
$this->configFile = $filename;
$config->setConfigFile($file);
} elseif (is_readable($filepath)) {
$this->configFile = $filepath;
$this->merge(new Zend_Config_Ini($filepath));
$config->setConfigFile($filepath);
$config->merge(new Zend_Config_Ini($filepath));
} else {
throw new NotReadableError(
'Cannot read config file "%s". Permission denied',
$filename
);
throw new NotReadableError('Cannot read config file "%s". Permission denied', $filepath);
}
return $config;
}
/**
@ -80,7 +77,7 @@ class Config extends Zend_Config
public static function app($configname = 'config', $fromDisk = false)
{
if (!isset(self::$app[$configname]) || $fromDisk) {
self::$app[$configname] = new Config(self::resolvePath($configname . '.ini'));
self::$app[$configname] = Config::fromIni(self::resolvePath($configname . '.ini'));
}
return self::$app[$configname];
}
@ -113,7 +110,7 @@ class Config extends Zend_Config
}
$moduleConfigs = self::$modules[$modulename];
if (!isset($moduleConfigs[$configname]) || $fromDisk) {
$moduleConfigs[$configname] = new Config(
$moduleConfigs[$configname] = Config::fromIni(
self::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
);
}
@ -138,15 +135,28 @@ class Config extends Zend_Config
}
/**
* Return the application wide config file
* Return this config's file path
*
* @return string
* @return string
*/
public function getConfigFile()
{
return $this->configFile;
}
/**
* Set this config's file path
*
* @param string $filepath The path to the config file
*
* @return self
*/
public function setConfigFile($filepath)
{
$this->configFile = $filepath;
return $this;
}
/**
* Prepend configuration base dir if input is relative
*

@ -918,10 +918,21 @@ class Module
* Translate a string with the global mt()
*
* @param $string
* @param null $context
*
* @return mixed|string
*/
protected function translate($string)
protected function translate($string, $context = null)
{
return mt($this->name, $string);
return mt($this->name, $string, $context);
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
protected function translatePlural($textSingular, $textPlural, $number, $context = null)
{
return mtp($this->name, $textSingular, $textPlural, $number, $context);
}
}

@ -278,7 +278,8 @@ class Web extends ApplicationBootstrap
if ($this->user !== null && $this->user->getPreferences() !== null) {
$detect = new TimezoneDetect();
$userTimezone = $this->user->getPreferences()->get('app.timezone', $detect->getTimezoneName());
$userTimezone = $this->user->getPreferences()
->getValue('icingaweb', 'timezone', $detect->getTimezoneName());
}
try {
@ -302,7 +303,7 @@ class Web extends ApplicationBootstrap
{
parent::setupInternationalization();
if ($this->user !== null && $this->user->getPreferences() !== null
&& (($locale = $this->user->getPreferences()->get('app.language')) !== null)
&& (($locale = $this->user->getPreferences()->getValue('icingaweb', 'language')) !== null)
) {
try {
Translator::setupLocale($locale);

@ -5,23 +5,85 @@
use Icinga\Util\Translator;
if (extension_loaded('gettext')) {
function t($messageId)
/**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function t($messageId, $context = null)
{
return Translator::translate($messageId, Translator::DEFAULT_DOMAIN);
return Translator::translate($messageId, Translator::DEFAULT_DOMAIN, $context);
}
function mt($domain, $messageId)
/**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function mt($domain, $messageId, $context = null)
{
return Translator::translate($messageId, $domain);
return Translator::translate($messageId, $domain, $context);
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function tp($messageId, $messageId2, $number, $context = null)
{
return Translator::translatePlural($messageId, $messageId2, $number, Translator::DEFAULT_DOMAIN, $context);
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function mtp($domain, $messageId, $messageId2, $number, $context = null)
{
return Translator::translatePlural($messageId, $messageId2, $number, $domain, $context);
}
} else {
function t($messageId)
/**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function t($messageId, $context = null)
{
return $messageId;
}
function mt($domain, $messageId)
/**
* (non-PHPDoc)
* @see Translator::translate() For the function documentation.
*/
function mt($domain, $messageId, $context = null)
{
return $messageId;
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function tp($messageId, $messageId2, $number, $context = null)
{
if ((int) $number !== 1) {
return $messageId2;
}
return $messageId;
}
/**
* (non-PHPDoc)
* @see Translator::translatePlural() For the function documentation.
*/
function mtp($domain, $messageId, $messageId2, $number, $context = null)
{
if ((int) $number !== 1) {
return $messageId2;
}
return $messageId;
}
}

@ -52,12 +52,8 @@ class AdmissionLoader
return $permissions;
}
foreach ($config as $section) {
if ($this->match($section, $username, $groups)) {
foreach ($section as $key => $value) {
if (strpos($key, 'permission') === 0) {
$permissions = array_merge($permissions, String::trimSplit($value));
}
}
if ($this->match($section, $username, $groups) && isset($section->permissions)) {
$permissions += String::trimSplit($section->permissions);
}
}
return $permissions;
@ -79,12 +75,12 @@ class AdmissionLoader
} catch (NotReadableError $e) {
return $restrictions;
}
foreach ($config as $section) {
foreach ($config as $name => $section) {
if ($this->match($section, $username, $groups)) {
if (!array_key_exists($section->name, $restrictions)) {
$restrictions[$section->name] = array();
}
$restrictions[$section->name][] = $section->restriction;
$restrictions[$section->name][$name] = $section->restriction;
}
}
return $restrictions;

@ -4,6 +4,7 @@
namespace Icinga\Authentication\Backend;
use Icinga\Logger\Logger;
use Icinga\User;
use Icinga\Authentication\UserBackend;
use Icinga\Protocol\Ldap\Connection;
@ -23,11 +24,14 @@ class LdapUserBackend extends UserBackend
protected $userNameAttribute;
public function __construct(Connection $conn, $userClass, $userNameAttribute)
protected $groupOptions;
public function __construct(Connection $conn, $userClass, $userNameAttribute, $groupOptions = null)
{
$this->conn = $conn;
$this->userClass = $userClass;
$this->userNameAttribute = $userNameAttribute;
$this->groupOptions = $groupOptions;
}
/**
@ -83,6 +87,43 @@ class LdapUserBackend extends UserBackend
}
}
/**
* Retrieve the user groups
*
* @TODO: Subject to change, see #7343
*
* @param string $dn
*
* @return array|null
*/
public function getGroups($dn)
{
if (empty($this->groupOptions) || ! isset($this->groupOptions['group_base_dn'])) {
return null;
}
$q = $this->conn->select()
->setBase($this->groupOptions['group_base_dn'])
->from(
$this->groupOptions['group_class'],
array($this->groupOptions['group_attribute'])
)
->where(
$this->groupOptions['group_member_attribute'],
$dn
);
$result = $this->conn->fetchAll($q);
$groups = array();
foreach ($result as $group) {
$groups[] = $group->{$this->groupOptions['group_attribute']};
}
return $groups;
}
/**
* Test whether the given user exists
*
@ -127,10 +168,15 @@ class LdapUserBackend extends UserBackend
return false;
}
try {
return $this->conn->testCredentials(
$this->conn->fetchDN($this->createQuery($user->getUsername())),
$userDn = $this->conn->fetchDN($this->createQuery($user->getUsername()));
$authenticated = $this->conn->testCredentials(
$userDn,
$password
);
if ($authenticated) {
$user->setGroups($this->getGroups($userDn));
}
return $authenticated;
} catch (LdapException $e) {
// Error during authentication of this specific user
throw new AuthenticationException(
@ -160,4 +206,3 @@ class LdapUserBackend extends UserBackend
);
}
}

@ -113,30 +113,32 @@ class Manager
}
/**
* Tries to authenticate the user with the current session
* Try to authenticate the user with the current session
*
* Authentication for externally-authenticated users will be revoked if the username changed or external
* authentication is no longer in effect
*/
public function authenticateFromSession()
{
$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) {
if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization();
}
}
}
/**
* Returns true when the user is currently authenticated
* Whether the user is authenticated
*
* @param Boolean $ignoreSession Set to true to prevent authentication by session
* @param bool $ignoreSession True to prevent session authentication
*
* @return bool
*/
public function isAuthenticated($ignoreSession = false)
{
if ($this->user === null && !$ignoreSession) {
if ($this->user === null && ! $ignoreSession) {
$this->authenticateFromSession();
}
return is_object($this->user);
@ -145,25 +147,16 @@ class Manager
/**
* Whether an authenticated user has a given permission
*
* This is true if the user owns this permission, false if not.
* Also false if there is no authenticated user
*
* TODO: I'd like to see wildcard support, e.g. module/*
*
* @param string $permission Permission name
* @return bool
*
* @return bool True if the user owns the given permission, false if not or if not authenticated
*/
public function hasPermission($permission)
{
if (! $this->isAuthenticated()) {
return false;
}
foreach ($this->user->getPermissions() as $p) {
if ($p === $permission) {
return true;
}
}
return false;
return $this->user->can($permission);
}
/**

@ -93,26 +93,44 @@ abstract class UserBackend implements Countable
$backend = new DbUserBackend($resource);
break;
case 'msldap':
$groupOptions = array(
'group_base_dn' => $backendConfig->group_base_dn,
'group_attribute' => $backendConfig->group_attribute,
'group_member_attribute' => $backendConfig->group_member_attribute,
'group_class' => $backendConfig->group_class
);
$backend = new LdapUserBackend(
$resource,
$backendConfig->get('user_class', 'user'),
$backendConfig->get('user_name_attribute', 'sAMAccountName')
$backendConfig->get('user_name_attribute', 'sAMAccountName'),
$groupOptions
);
break;
case 'ldap':
if (($userClass = $backendConfig->user_class) === null) {
if ($backendConfig->user_class === null) {
throw new ConfigurationError(
'Authentication configuration for backend "%s" is missing the user_class directive',
$name
);
}
if (($userNameAttribute = $backendConfig->user_name_attribute) === null) {
if ($backendConfig->user_name_attribute === null) {
throw new ConfigurationError(
'Authentication configuration for backend "%s" is missing the user_name_attribute directive',
$name
);
}
$backend = new LdapUserBackend($resource, $userClass, $userNameAttribute);
$groupOptions = array(
'group_base_dn' => $backendConfig->group_base_dn,
'group_attribute' => $backendConfig->group_attribute,
'group_member_attribute' => $backendConfig->group_member_attribute,
'group_class' => $backendConfig->group_class
);
$backend = new LdapUserBackend(
$resource,
$backendConfig->user_class,
$backendConfig->user_name_attribute,
$groupOptions
);
break;
default:
throw new ConfigurationError(

@ -108,6 +108,18 @@ class Params
return $this->standalone;
}
/**
* Support isset() and empty() checks on options
*
* @param $name
*
* @return bool
*/
public function __isset($name)
{
return isset($this->params[$name]);
}
/**
* @see Params::get()
*/

@ -59,6 +59,13 @@ class DbQuery extends SimpleQuery
*/
protected $count;
/**
* GROUP BY clauses
*
* @var string|array
*/
protected $group;
protected function init()
{
$this->db = $this->ds->getDbAdapter();
@ -89,17 +96,21 @@ class DbQuery extends SimpleQuery
public function getSelectQuery()
{
$select = $this->dbSelect();
// Add order fields to select for postgres distinct queries (#6351)
if ($this->hasOrder()
&& $this->getDatasource()->getDbType() === 'pgsql'
&& $select->getPart(Zend_Db_Select::DISTINCT) === true) {
foreach ($this->getOrder() as $fieldAndDirection) {
list($alias, $field) = explode('.', $fieldAndDirection[0]);
$this->columns[$field] = $fieldAndDirection[0];
if (array_search($fieldAndDirection[0], $this->columns) === false) {
$this->columns[] = $fieldAndDirection[0];
}
}
}
if ($this->group) {
$select->group($this->group);
}
$select->columns($this->columns);
$this->applyFilterSql($select);
@ -117,7 +128,7 @@ class DbQuery extends SimpleQuery
return $select;
}
protected function applyFilterSql($query)
public function applyFilterSql($query)
{
$where = $this->renderFilter($this->filter);
if ($where !== '') {
@ -223,7 +234,7 @@ class DbQuery extends SimpleQuery
*/
public function isTimestamp($field)
{
return $this;
return false;
}
public function whereToSql($col, $sign, $expression)
@ -253,6 +264,7 @@ class DbQuery extends SimpleQuery
$this->applyFilterSql($count);
if ($this->useSubqueryCount) {
$count->columns($this->columns);
$columns = array('cnt' => 'COUNT(*)');
return $this->db->select()->from($count, $columns);
}
@ -300,4 +312,17 @@ class DbQuery extends SimpleQuery
{
return (string) $this->getSelectQuery();
}
/**
* Add a GROUP BY clause
*
* @param string|array $group
*
* @return $this
*/
public function group($group)
{
$this->group = $group;
return $this;
}
}

@ -12,6 +12,8 @@ class FilterExpression extends Filter
public function __construct($column, $sign, $expression)
{
$column = trim($column);
$expression = is_array($expression) ? array_map('trim', $expression) : trim($expression);
$this->column = $column;
$this->sign = $sign;
$this->expression = $expression;

@ -10,7 +10,6 @@ use Icinga\Util\ConfigAwareFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Data\Db\DbConnection;
use Icinga\Protocol\Livestatus\Connection as LivestatusConnection;
use Icinga\Protocol\Statusdat\Reader as StatusdatReader;
use Icinga\Protocol\Ldap\Connection as LdapConnection;
use Icinga\Protocol\File\FileReader;
@ -102,7 +101,7 @@ class ResourceFactory implements ConfigAwareFactory
*
* @param Zend_Config $config The configuration for the created resource.
*
* @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader An objects that can be used to access
* @return DbConnection|LdapConnection|LivestatusConnection An object that can be used to access
* the given resource. The returned class depends on the configuration property 'type'.
* @throws ConfigurationError When an unsupported type is given
*/
@ -115,9 +114,6 @@ class ResourceFactory implements ConfigAwareFactory
case 'ldap':
$resource = new LdapConnection($config);
break;
case 'statusdat':
$resource = new StatusdatReader($config);
break;
case 'livestatus':
$resource = new LivestatusConnection($config->socket);
break;
@ -137,7 +133,7 @@ class ResourceFactory implements ConfigAwareFactory
* Create a resource from name
*
* @param string $resourceName
* @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader
* @return DbConnection|LdapConnection|LivestatusConnection
*/
public static function create($resourceName)
{

@ -0,0 +1,10 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Exception;
/**
* Exception thrown if a property does not exist
*/
class InvalidPropertyException extends IcingaException {}

@ -0,0 +1,12 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Exception;
/**
* Exception thrown if a query contains invalid parameters
*/
class QueryException extends IcingaException
{
}

@ -7,58 +7,109 @@ namespace Icinga\Logger;
use Exception;
use Zend_Config;
use Icinga\Exception\ConfigurationError;
use Icinga\Logger\Writer\FileWriter;
use Icinga\Logger\Writer\SyslogWriter;
/**
* Singleton logger
* Logger
*/
class Logger
{
/**
* Debug message
*/
const DEBUG = 1;
/**
* Informational message
*/
const INFO = 2;
/**
* Warning message
*/
const WARNING = 4;
/**
* Error message
*/
const ERROR = 8;
/**
* Log levels
*
* @var array
*/
public static $levels = array(
Logger::DEBUG => 'DEBUG',
Logger::INFO => 'INFO',
Logger::WARNING => 'WARNING',
Logger::ERROR => 'ERROR'
);
/**
* This logger's instance
*
* @var Logger
* @var static
*/
protected static $instance;
/**
* The log writer to use
* Log writer
*
* @var \Icinga\Logger\LogWriter
*/
protected $writer;
/**
* The configured type
*
* @string Type (syslog, file)
*/
protected $type = 'none';
/**
* The maximum severity to emit
* Maximum level to emit
*
* @var int
*/
protected $verbosity;
/**
* The supported severities
*/
public static $ERROR = 0;
public static $WARNING = 1;
public static $INFO = 2;
public static $DEBUG = 3;
protected $level;
/**
* Create a new logger object
*
* @param Zend_Config $config
* @param Zend_Config $config
*
* @throws ConfigurationError If the logging configuration directive 'log' is missing or if the logging level is
* not defined
*/
public function __construct(Zend_Config $config)
{
$this->verbosity = $config->level;
if ($config->log === null) {
throw new ConfigurationError('Required logging configuration directive \'log\' missing');
}
if ($config->enable) {
if (($level = $config->level) !== null) {
if (is_numeric($level)) {
if (! isset(static::$levels[(int) $level])) {
throw new ConfigurationError(
'Can\'t set logging level %d. Logging level is not defined. Use one of %s or one of the'
. ' Logger\'s constants.',
$level,
implode(', ', array_keys(static::$levels))
);
}
$this->level = static::$levels[(int) $level];
} else {
$level = strtoupper($level);
$levels = array_flip(static::$levels);
if (! isset($levels[$level])) {
throw new ConfigurationError(
'Can\'t set logging level "%s". Logging level is not defined. Use one of %s.',
$level,
implode(', ', array_keys($levels))
);
}
$this->level = $levels[$level];
}
} else {
$this->level = static::ERROR;
}
if (strtolower($config->get('log', 'syslog')) !== 'none') {
$this->writer = $this->createWriter($config);
}
}
@ -67,14 +118,17 @@ class Logger
* Create a new logger object
*
* @param Zend_Config $config
*
* @return static
*/
public static function create(Zend_Config $config)
{
static::$instance = new static($config);
return static::$instance;
}
/**
* Return a log writer
* Create a log writer
*
* @param Zend_Config $config The configuration to initialize the writer with
*
@ -83,28 +137,26 @@ class Logger
*/
protected function createWriter(Zend_Config $config)
{
$class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->type)) . 'Writer';
if (!class_exists($class)) {
$class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->log)) . 'Writer';
if (! class_exists($class)) {
throw new ConfigurationError(
'Cannot find log writer of type "%s"',
$config->type
$config->log
);
}
$this->type = $config->type;
return new $class($config);
}
/**
* Write a message to the log
* Log a message
*
* @param string $message The message to write
* @param int $severity The severity to use
* @param int $level The logging level
* @param string $message The log message
*/
public function log($message, $severity)
public function log($level, $message)
{
if ($this->writer !== null && $this->verbosity >= $severity) {
$this->writer->log($severity, $message);
if ($this->writer !== null && $this->level >= $level) {
$this->writer->log($level, $message);
}
}
@ -162,7 +214,7 @@ class Logger
public static function error()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$ERROR);
static::$instance->log(static::ERROR, static::formatMessage(func_get_args()));
}
}
@ -174,7 +226,7 @@ class Logger
public static function warning()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$WARNING);
static::$instance->log(static::WARNING, static::formatMessage(func_get_args()));
}
}
@ -186,7 +238,7 @@ class Logger
public static function info()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$INFO);
static::$instance->log(static::INFO, static::formatMessage(func_get_args()));
}
}
@ -198,7 +250,7 @@ class Logger
public static function debug()
{
if (static::$instance !== null && func_num_args() > 0) {
static::$instance->log(static::formatMessage(func_get_args()), static::$DEBUG);
static::$instance->log(static::DEBUG, static::formatMessage(func_get_args()));
}
}
@ -212,20 +264,30 @@ class Logger
return $this->writer;
}
/**
* Is the logger writing to Syslog?
*
* @return bool
*/
public static function writesToSyslog()
{
return static::$instance && static::$instance->type === 'syslog';
return static::$instance && static::$instance instanceof SyslogWriter;
}
/**
* Is the logger writing to a file?
*
* @return bool
*/
public static function writesToFile()
{
return static::$instance && static::$instance->type === 'file';
return static::$instance && static::$instance instanceof FileWriter;
}
/**
* Get this' instance
*
* @return Logger
* @return static
*/
public static function getInstance()
{

@ -5,38 +5,43 @@
namespace Icinga\Logger\Writer;
use Exception;
use Icinga\Exception\IcingaException;
use Zend_Config;
use Icinga\Util\File;
use Icinga\Exception\ConfigurationError;
use Icinga\Logger\Logger;
use Icinga\Logger\LogWriter;
use Icinga\Exception\ConfigurationError;
use Icinga\Util\File;
/**
* Class to write log messages to a file
* Log to a file
*/
class FileWriter extends LogWriter
{
/**
* The path to the file
* Path to the file
*
* @var string
*/
protected $path;
protected $file;
/**
* Create a new log writer initialized with the given configuration
* Create a new file log writer
*
* @throws ConfigurationError In case the log path does not exist
* @param Zend_Config $config
*
* @throws ConfigurationError If the configuration directive 'file' is missing or if the path to 'file' does
* not exist or if writing to 'file' is not possible
*/
public function __construct(Zend_Config $config)
{
$this->path = $config->target;
if ($config->file === null) {
throw new ConfigurationError('Required logging configuration directive \'file\' missing');
}
$this->file = $config->file;
if (substr($this->path, 0, 6) !== 'php://' && false === file_exists(dirname($this->path))) {
if (substr($this->file, 0, 6) !== 'php://' && ! file_exists(dirname($this->file))) {
throw new ConfigurationError(
'Log path "%s" does not exist',
dirname($this->path)
dirname($this->file)
);
}
@ -45,70 +50,32 @@ class FileWriter extends LogWriter
} catch (Exception $e) {
throw new ConfigurationError(
'Cannot write to log file "%s" (%s)',
$this->path,
$this->file,
$e->getMessage()
);
}
}
/**
* Log a message with the given severity
* Log a message
*
* @param int $severity The severity to use
* @param string $message The message to log
* @param int $level The logging level
* @param string $message The log message
*/
public function log($severity, $message)
public function log($level, $message)
{
$this->write(date('c') . ' ' . $this->getSeverityString($severity) . ' ' . $message . PHP_EOL);
$this->write(date('c') . ' - ' . Logger::$levels[$level] . ' - ' . $message . PHP_EOL);
}
/**
* Return a string representation for the given severity
* Write a message to the log
*
* @param string $severity The severity to use
*
* @return string The string representation of the severity
*
* @throws IcingaException In case the given severity is unknown
* @param string $message
*/
protected function getSeverityString($severity)
protected function write($message)
{
switch ($severity) {
case Logger::$ERROR:
return '- ERROR -';
case Logger::$WARNING:
return '- WARNING -';
case Logger::$INFO:
return '- INFO -';
case Logger::$DEBUG:
return '- DEBUG -';
default:
throw new IcingaException(
'Unknown severity "%s"',
$severity
);
}
}
/**
* Write a message to the path
*
* @param string $text The message to write
*
* @throws Exception In case write acess to the path failed
*/
protected function write($text)
{
$file = new File($this->path, 'a');
$file->fwrite($text);
$file = new File($this->file, 'a');
$file->fwrite($message);
$file->fflush();
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
}

@ -4,27 +4,24 @@
namespace Icinga\Logger\Writer;
use Exception;
use Zend_Config;
use Icinga\Logger\Logger;
use Icinga\Logger\LogWriter;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\IcingaException;
/**
* Class to write messages to syslog
* Log to the syslog service
*/
class SyslogWriter extends LogWriter
{
/**
* The facility where to write messages to
* Syslog facility
*
* @var string
* @var int
*/
protected $facility;
/**
* The prefix to prepend to each message
* Prefix to prepend to each message
*
* @var string
*/
@ -35,79 +32,42 @@ class SyslogWriter extends LogWriter
*
* @var array
*/
protected $facilities = array(
'LOG_USER' => LOG_USER
public static $facilities = array(
'user' => LOG_USER
);
/**
* Create a new log writer initialized with the given configuration
* Log level to syslog severity map
*
* @var array
*/
public static $severityMap = array(
Logger::ERROR => LOG_ERR,
Logger::WARNING => LOG_WARNING,
Logger::INFO => LOG_INFO,
Logger::DEBUG => LOG_DEBUG
);
/**
* Create a new syslog log writer
*
* @param Zend_Config $config
*/
public function __construct(Zend_Config $config)
{
if (!array_key_exists($config->facility, $this->facilities)) {
throw new ConfigurationError(
'Cannot create syslog writer with unknown facility "%s"',
$config->facility
);
}
$this->ident = $config->application;
$this->facility = $this->facilities[$config->facility];
$this->ident = $config->get('application', 'icingaweb');
$this->facility = static::$facilities['user'];
}
/**
* Log a message with the given severity
* Log a message
*
* @param int $severity The severity to use
* @param string $message The message to log
*
* @throws Exception In case the given severity cannot be mapped to a valid syslog priority
* @param int $level The logging level
* @param string $message The log message
*/
public function log($severity, $message)
public function log($level, $message)
{
$priorities = array(
Logger::$ERROR => LOG_ERR,
Logger::$WARNING => LOG_WARNING,
Logger::$INFO => LOG_INFO,
Logger::$DEBUG => LOG_DEBUG
);
if (!array_key_exists($severity, $priorities)) {
throw new IcingaException(
'Severity "%s" cannot be mapped to a valid syslog priority',
$severity
);
}
$this->open();
$this->write($priorities[$severity], $message);
$this->close();
}
/**
* Open a new syslog connection
*/
protected function open()
{
openlog($this->ident, 0, $this->facility);
}
/**
* Write a message to the syslog connection
*
* @param int $priority The priority to use
* @param string $message The message to write
*/
protected function write($priority, $message)
{
syslog($priority, $message);
}
/**
* Close the syslog connection
*/
protected function close()
{
closelog();
openlog($this->ident, LOG_PID, $this->facility);
syslog(static::$severityMap[$level], $message);
}
}

@ -1,177 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe;
use Icinga\Exception\ProgrammingError;
/**
* Base class for any concrete command implementation
*/
abstract class Command
{
/**
* Whether hosts are ignored in case of a host- or servicegroup
*
* @var bool
*/
protected $withoutHosts = false;
/**
* Whether services are ignored in case of a host- or servicegroup
*
* @var bool
*/
protected $withoutServices = false;
/**
* Whether child hosts are going to be included in case of a host command
*
* @var bool
*/
protected $withChildren = false;
/**
* Whether only services are going to be included in case of a host command
*
* @var bool
*/
protected $onlyServices = false;
/**
* Whether it is a global command or not
*
* @var bool
*/
protected $globalCommand = false;
/**
* Set whether this command should only affect the services of a host- or servicegroup
*
* @param bool $state
* @return self
*/
public function excludeHosts($state = true)
{
$this->withoutHosts = (bool) $state;
return $this;
}
/**
* Set whether this command should only affect the hosts of a host- or servicegroup
*
* @param bool $state
* @return self
*/
public function excludeServices($state = true)
{
$this->withoutServices = (bool) $state;
return $this;
}
/**
* Set whether this command should also affect all children hosts of a host
*
* @param bool $state
* @return self
*/
public function includeChildren($state = true)
{
$this->withChildren = (bool) $state;
return $this;
}
/**
* Set whether this command only affects the services associated with a particular host
*
* @param bool $state
* @return self
*/
public function excludeHost($state = true)
{
$this->onlyServices = (bool) $state;
return $this;
}
/**
* Getter for flag whether a command is global
* @return bool
*/
public function provideGlobalCommand()
{
return (bool) $this->globalCommand;
}
/**
* Return this command's arguments in the order expected by the actual command definition
*
* @return array
*/
abstract public function getArguments();
/**
* Return the command as a string with the given host being inserted
*
* @param string $hostname The name of the host to insert
*
* @return string The string representation of the command
*/
abstract public function getHostCommand($hostname);
/**
* Return the command as a string with the given host and service being inserted
*
* @param string $hostname The name of the host to insert
* @param string $servicename The name of the service to insert
*
* @return string The string representation of the command
*/
abstract public function getServiceCommand($hostname, $servicename);
/**
* Return the command as a string with the given hostgroup being inserted
*
* @param string $hostgroup The name of the hostgroup to insert
*
* @return string The string representation of the command
*/
public function getHostgroupCommand($hostgroup)
{
throw new ProgrammingError(
'%s does not provide a hostgroup command',
get_class($this)
);
}
/**
* Return the command as a string with the given servicegroup being inserted
*
* @param string $servicegroup The name of the servicegroup to insert
*
* @return string The string representation of the command
*/
public function getServicegroupCommand($servicegroup)
{
throw new ProgrammingError(
'%s does not provide a servicegroup command',
get_class($this)
);
}
/**
* Return the command as a string for the whole instance
*
* @param string $instance
*
* @return string
* @throws ProgrammingError
*/
public function getGlobalCommand($instance = null)
{
throw new ProgrammingError(
'%s does not provide a global command',
getclass($this)
);
}
}

@ -1,603 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe;
use Icinga\Protocol\Commandpipe\Transport\Transport;
use Icinga\Protocol\Commandpipe\Transport\LocalPipe;
use Icinga\Protocol\Commandpipe\Transport\SecureShell;
/**
* Class to the access icinga CommandPipe via a @see Icinga\Protocol\Commandpipe\Transport.php
*
* Will be configured using the instances.ini
*/
class CommandPipe
{
/**
* The name of this class as defined in the instances.ini
*
* @var string
*/
private $name = "";
/**
* The underlying @see Icinga\Protocol\Commandpipe\Transport.php class handling communication with icinga
*
* @var Icinga\Protocol\Commandpipe\Transport
*/
private $transport = null;
/**
* Constant identifying a monitoring object as host
*/
const TYPE_HOST = "HOST";
/**
* Constant identifying a monitoring object as service
*/
const TYPE_SERVICE = "SVC";
/**
* Constant identifying a monitoring object as hostgroup
*/
const TYPE_HOSTGROUP = "HOSTGROUP";
/**
* Constant identifying a monitoring object as servicegroups
*/
const TYPE_SERVICEGROUP = "SERVICEGROUP";
/**
* Notification option (use logical OR for combination)
*
* Broadcast (send notification to all normal and all escalated contacts for the service)
*/
const NOTIFY_BROADCAST = 1;
/**
* Notification option (use logical OR for combination)
*
* notification is sent out regardless of current time, whether or not notifications are enabled, etc.
*/
const NOTIFY_FORCED = 2;
/**
* Notification option (use logical OR for combination)
*
* Increment current notification # for the service(this is not done by default for custom notifications)
*/
const NOTIFY_INCREMENT = 4;
/**
* Create a new CommandPipe class which accesses the icinga.cmd pipe as defined in $config
*
* @param \Zend_Config $config
*/
public function __construct(\Zend_Config $config)
{
$this->getTransportForConfiguration($config);
$this->name = $config->name;
}
/**
* Setup the @see Icinga\Protocol\Commandpipe\Transport.php class that will be used for accessing the command pipe
*
* Currently this method uses SecureShell when a host is given, otherwise it assumes the pipe is accessible
* via the machines filesystem
*
* @param \Zend_Config $config The configuration as defined in the instances.ini
*/
private function getTransportForConfiguration(\Zend_Config $config)
{
if (isset($config->host)) {
$this->transport = new SecureShell();
$this->transport->setEndpoint($config);
} else {
$this->transport = new LocalPipe();
$this->transport->setEndpoint($config);
}
}
/**
* Send the command string $command to the icinga pipe
*
* This method just delegates the send command to the underlying transport
*
* @param String $command The command string to send, without the timestamp
*/
public function 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);
}
/**
* Send a command to the icinga pipe
*
* @param Command $command
* @param array $objects
*/
public function sendCommand(Command $command, array $objects = array())
{
if ($command->provideGlobalCommand() === true) {
$this->send($command->getGlobalCommand());
} else {
foreach ($objects as $object) {
$objectType = $this->getObjectType($object);
if ($objectType === self::TYPE_SERVICE) {
$this->send($command->getServiceCommand($object->host_name, $object->service_description));
} else {
$this->send($command->getHostCommand($object->host_name));
}
}
}
}
/**
* Remove the acknowledgements of the provided objects
*
* @param array $objects An array of mixed service and host objects whose acknowledgments will be removed
*/
public function removeAcknowledge($objects)
{
foreach ($objects as $object) {
if (isset($object->service_description)) {
$this->send("REMOVE_SVC_ACKNOWLEDGEMENT;$object->host_name;$object->service_description");
} else {
$this->send("REMOVE_HOST_ACKNOWLEDGEMENT;$object->host_name");
}
}
}
/**
* Removes the submitted comments
*
* @param array $objectsOrComments An array of hosts and services (to remove all their comments)
* or single comment objects to remove
*/
public function removeComment($objectsOrComments)
{
foreach ($objectsOrComments as $object) {
if (isset($object->comment_id)) {
if (isset($object->service_description)) {
$type = "SERVICE_COMMENT";
} else {
$type = "HOST_COMMENT";
}
$this->send("DEL_{$type};" . intval($object->comment_id));
} else {
if (isset($object->service_description)) {
$type = "SERVICE_COMMENT";
} else {
$type = "HOST_COMMENT";
}
$cmd = "DEL_ALL_{$type}S;" . $object->host_name;
if ($type == "SERVICE_COMMENT") {
$cmd .= ";" . $object->service_description;
}
$this->send($cmd);
}
}
}
/**
* Globally enable notifications for this instance
*
*/
public function enableGlobalNotifications()
{
$this->send("ENABLE_NOTIFICATIONS");
}
/**
* Globally disable notifications for this instance
*
*/
public function disableGlobalNotifications()
{
$this->send("DISABLE_NOTIFICATIONS");
}
/**
* Return the object type of the provided object (TYPE_SERVICE or TYPE_HOST)
*
* @param $object The object to identify
* @return string TYPE_SERVICE or TYPE_HOST
*/
private function getObjectType($object)
{
//@TODO: This must be refactored once more commands are supported
if (isset($object->service_description)) {
return self::TYPE_SERVICE;
}
return self::TYPE_HOST;
}
/**
* Remove downtimes for objects
*
* @param array $objects An array containing hosts, service or downtime objects
* @param int $starttime An optional starttime to use for the DEL_DOWNTIME_BY_HOST_NAME command
*/
public function removeDowntime($objects, $starttime = 0)
{
foreach ($objects as $object) {
$type = $this->getObjectType($object);
if (isset($object->downtime_id)) {
$this->send("DEL_" . $type . "_DOWNTIME;" . $object->downtime_id);
continue;
}
$cmd = "DEL_DOWNTIME_BY_HOST_NAME;" . $object->host_name;
if ($type == self::TYPE_SERVICE) {
$cmd .= ";" . $object->service_description;
}
if ($starttime != 0) {
$cmd .= ";" . $starttime;
}
$this->send($cmd);
}
}
/**
* Restart the icinga instance
*
*/
public function restartIcinga()
{
$this->send("RESTART_PROCESS");
}
/**
* Modify monitoring flags for the provided objects
*
* @param array $objects An arry of service and/or host objects to modify
* @param PropertyModifier $flags The Monitoring attributes to modify
*/
public function setMonitoringProperties($objects, PropertyModifier $flags)
{
foreach ($objects as $object) {
$type = $this->getObjectType($object);
$formatArray = $flags->getFormatString($type);
foreach ($formatArray as $format) {
$format .= ";"
. $object->host_name
. ($type == self::TYPE_SERVICE ? ";" . $object->service_description : "");
$this->send($format);
}
}
}
/**
* Enable active checks for all provided objects
*
* @param array $objects An array containing services and hosts to enable active checks for
*/
public function enableActiveChecks($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::ACTIVE => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Disable active checks for all provided objects
*
* @param array $objects An array containing services and hosts to disable active checks
*/
public function disableActiveChecks($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::ACTIVE => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Enable passive checks for all provided objects
*
* @param array $objects An array containing services and hosts to enable passive checks for
*/
public function enablePassiveChecks($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::PASSIVE => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Enable passive checks for all provided objects
*
* @param array $objects An array containing services and hosts to enable passive checks for
*/
public function disablePassiveChecks($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::PASSIVE => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Enable flap detection for all provided objects
*
* @param array $objects An array containing services and hosts to enable flap detection
*
*/
public function enableFlappingDetection($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::FLAPPING => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Disable flap detection for all provided objects
*
* @param array $objects An array containing services and hosts to disable flap detection
*
*/
public function disableFlappingDetection($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::FLAPPING => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Enable notifications for all provided objects
*
* @param array $objects An array containing services and hosts to enable notification
*
*/
public function enableNotifications($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::NOTIFICATIONS => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Disable flap detection for all provided objects
*
* @param array $objects An array containing services and hosts to disable notifications
*
*/
public function disableNotifications($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::NOTIFICATIONS => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Enable freshness checks for all provided objects
*
* @param array $objects An array of hosts and/or services
*/
public function enableFreshnessChecks($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::FRESHNESS => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Disable freshness checks for all provided objects
*
* @param array $objects An array of hosts and/or services
*/
public function disableFreshnessChecks($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::FRESHNESS => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Enable event handler for all provided objects
*
* @param array $objects An array of hosts and/or services
*/
public function enableEventHandler($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::EVENTHANDLER => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Disable event handler for all provided objects
*
* @param array $objects An array of hosts and/or services
*/
public function disableEventHandler($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::EVENTHANDLER => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Enable performance data parsing for all provided objects
*
* @param array $objects An array of hosts and/or services
*/
public function enablePerfdata($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::PERFDATA => PropertyModifier::STATE_ENABLE
)
)
);
}
/**
* Disable performance data parsing for all provided objects
*
* @param array $objects An array of hosts and/or services
*/
public function disablePerfdata($objects)
{
$this->setMonitoringProperties(
$objects,
new PropertyModifier(
array(
PropertyModifier::PERFDATA => PropertyModifier::STATE_DISABLE
)
)
);
}
/**
* Disable notifications for all services of the provided hosts
*
* @param array $objects An array of hosts
*/
public function disableNotificationsForServices($objects)
{
foreach ($objects as $host) {
$msg = 'DISABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name;
$this->send($msg);
}
}
/**
* Enable notifications for all services of the provided hosts
*
* @param array $objects An array of hosts
*/
public function enableNotificationsForServices($objects)
{
foreach ($objects as $host) {
$msg = 'ENABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name;
$this->send($msg);
}
}
/**
* Disable active checks for all services of the provided hosts
*
* @param array $objects An array of hosts
*/
public function disableActiveChecksWithChildren($objects)
{
foreach ($objects as $host) {
$msg = 'DISABLE_HOST_SVC_CHECKS;'.$host->host_name;
$this->send($msg);
}
}
/**
* Enable active checks for all services of the provided hosts
*
* @param array $objects An array of hosts
*/
public function enableActiveChecksWithChildren($objects)
{
foreach ($objects as $host) {
$msg = 'ENABLE_HOST_SVC_CHECKS;'.$host->host_name;
$this->send($msg);
}
}
/**
* Reset modified attributes for all provided objects
*
* @param array $objects An array of hosts and services
*/
public function resetAttributes($objects)
{
foreach ($objects as $object) {
$type = $this->getObjectType($object);
if ($type === self::TYPE_SERVICE) {
$this->send('CHANGE_SVC_MODATTR;'.$object->host_name.';'.$object->service_description.';0');
} else {
$this->send('CHANGE_HOST_MODATTR;'.$object->host_name.';0');
}
}
}
/**
* Return the transport handler that handles actual sending of commands
*
* @return Transport
*/
public function getTransport()
{
return $this->transport;
}
}

@ -1,61 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe;
/**
* Container for comment information that can be send to Icinga's external command pipe
*/
class Comment
{
/**
* Whether this comment is persistent or not
*
* @var bool
*/
public $persistent;
/**
* The author of this comment
*
* @var string
*/
public $author;
/**
* The text of this comment
*
* @var string
*/
public $content;
/**
* Create a new comment object
*
* @param string $author The name of the comment's author
* @param string $content The text for this comment
* @param bool $persistent Whether this comment should be persistent or not
*/
public function __construct($author, $content, $persistent = false)
{
$this->author = $author;
$this->content = $content;
$this->persistent = $persistent;
}
/**
* Return this comment's properties as list of command parameters
*
* @param bool $ignorePersistentFlag Whether the persistent flag should be included or not
* @return array
*/
public function getArguments($ignorePersistentFlag = false)
{
if ($ignorePersistentFlag) {
return array($this->author, $this->content);
} else {
return array($this->persistent ? '1' : '0', $this->author, $this->content);
}
}
}

@ -1,12 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe\Exception;
/**
* Exception class for unknown/invalid external commands
*/
class InvalidCommandException extends \Exception
{
}

@ -1,110 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe;
/**
* Container class to modify a few monitoring attributes at oncee
*
*/
class PropertyModifier
{
/**
* Set an attribute to be enabled in the command
*/
const STATE_ENABLE = 1;
/**
* Set an attribute to be disabled in the command
*/
const STATE_DISABLE = 0;
/**
* Set an attribute to not be modified in the command
*/
const STATE_KEEP = -1;
/**
* Template for enabling/disabling flap detection
*/
const FLAPPING = "%s_FLAP_DETECTION";
/**
* Template for enabling/disabling active checks
*/
const ACTIVE = "%s_CHECK";
/**
* Template for enabling/disabling passive checks
*/
const PASSIVE = "PASSIVE_%s_CHECKS";
/**
* Template for enabling/disabling notification
*/
const NOTIFICATIONS = "%s_NOTIFICATIONS";
/**
* Template for enabling/disabling freshness checks
*/
const FRESHNESS = "%s_FRESHNESS_CHECKS";
/**
* Template for enabling/disabling event handler
*/
const EVENTHANDLER = "%s_EVENT_HANDLER";
/**
* The state that will be applied when fetching this container for an object
*
* @var array
*/
private $flags = array(
self::FLAPPING => self::STATE_KEEP,
self::ACTIVE => self::STATE_KEEP,
self::PASSIVE => self::STATE_KEEP,
self::NOTIFICATIONS => self::STATE_KEEP,
self::FRESHNESS => self::STATE_KEEP,
self::EVENTHANDLER => self::STATE_KEEP
);
/**
* Create a new PropertyModified object using the given flags
*
* @param array $flags Flags to enable/disable/keep different monitoring attributes
*/
public function __construct(array $flags)
{
foreach ($flags as $type => $value) {
if (isset($this->flags[$type])) {
$this->flags[$type] = $value;
}
}
}
/**
* Return this object as a template for the given object type
*
* @param $type Either CommandPipe::TYPE_HOST or CommandPipe::TYPE_SERVICE
* @return array An array of external command templates for the given type representing the containers state
*/
public function getFormatString($type)
{
$cmd = array();
foreach ($this->flags as $cmdTemplate => $setting) {
if ($setting == self::STATE_KEEP) {
continue;
}
$commandString = ($setting == self::STATE_ENABLE ? "ENABLE_" : "DISABLE_");
$targetString = $type;
if ($type == CommandPipe::TYPE_SERVICE && $cmdTemplate == self::FRESHNESS) {
// the external command definition is inconsistent here..
$targetString = "SERVICE";
}
$commandString .= sprintf($cmdTemplate, $targetString);
$cmd[] = $commandString;
}
return $cmd;
}
}

@ -1,70 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe\Transport;
use Exception;
use Icinga\Util\File;
use Icinga\Logger\Logger;
use Icinga\Exception\ConfigurationError;
/**
* CommandPipe Transport class that writes to a file accessible by the filesystem
*/
class LocalPipe implements Transport
{
/**
* The path of the icinga commandpipe
*
* @var String
*/
private $path;
/**
* The mode to use to access the pipe
*
* @var string
*/
private $openMode = "wn";
/**
* @see Transport::setEndpoint()
*/
public function setEndpoint(\Zend_Config $config)
{
$this->path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd';
}
/**
* @see Transport::send()
*/
public function send($message)
{
Logger::debug('Attempting to send external icinga command %s to local command file ', $message, $this->path);
try {
$file = new File($this->path, $this->openMode);
$file->fwrite('[' . time() . '] ' . $message . PHP_EOL);
$file->fflush();
} catch (Exception $e) {
throw new ConfigurationError(
'Could not open icinga command pipe at "%s" (%s)',
$this->path,
$e->getMessage()
);
}
Logger::debug('Command sent: [' . time() . '] ' . $message . PHP_EOL);
}
/**
* Overwrite the open mode (useful for testing)
*
* @param string $mode The mode to use to access the pipe
*/
public function setOpenMode($mode)
{
$this->openMode = $mode;
}
}

@ -1,102 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe\Transport;
use RuntimeException;
use Zend_Config;
use Icinga\Logger\Logger;
/**
* Command pipe transport class that uses ssh for connecting to a remote filesystem with the icinga.cmd pipe
*
* The remote host must have KeyAuth enabled for this user
*/
class SecureShell implements Transport
{
/**
* The remote host to connect to
*
* @var string
*/
private $host = 'localhost';
/**
* The location of the icinga pipe on the remote host
*
* @var string
*/
private $path = "/usr/local/icinga/var/rw/icinga.cmd";
/**
* The SSH port of the remote host
*
* @var int
*/
private $port = 22;
/**
* The user to authenticate with on the remote host
*
* @var String
*/
private $user = null;
/**
* Overwrite the target file of this Transport class using the given config from instances.ini
*
* @param Zend_Config $config
*
* @see Transport::setEndpoint()
*/
public function setEndpoint(Zend_Config $config)
{
$this->host = isset($config->host) ? $config->host : 'localhost';
$this->port = isset($config->port) ? $config->port : 22;
$this->user = isset($config->user) ? $config->user : null;
$this->path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd';
}
/**
* Write the given external command to the command pipe
*
* @param string $command
*
* @throws RuntimeException When the command could not be sent to the remote Icinga host
* @see Transport::send()
*/
public function send($command)
{
$retCode = 0;
$output = array();
Logger::debug(
'Icinga instance is on different host, attempting to send command %s via ssh to %s:%s/%s',
$command,
$this->host,
$this->port,
$this->path
);
$hostConnector = $this->user ? $this->user . "@" . $this->host : $this->host;
$command = escapeshellarg('['. time() .'] ' . $command);
$sshCommand = sprintf(
'ssh -o BatchMode=yes -o KbdInteractiveAuthentication=no %s -p %d'
. ' "echo %s > %s" 2>&1',
$hostConnector,
$this->port,
$command,
$this->path
);
exec($sshCommand, $output, $retCode);
Logger::debug("Command '%s' exited with %d: %s", $sshCommand, $retCode, $output);
if ($retCode != 0) {
$msg = 'Could not send command to remote Icinga host: '
. implode(PHP_EOL, $output)
. " (returncode $retCode)";
Logger::error($msg);
throw new RuntimeException($msg);
}
}
}

@ -1,27 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Commandpipe\Transport;
use Zend_Config;
/**
* Interface for Transport classes handling the concrete access to the command pipe
*/
interface Transport
{
/**
* Overwrite the target file of this Transport class using the given config from instances.ini
*
* @param Zend_Config $config A configuration file containing a 'path' setting
*/
public function setEndpoint(Zend_Config $config);
/**
* Write the given external command to the command pipe
*
* @param string $message The command to send, without the timestamp (this will be added here)
*/
public function send($message);
}

@ -4,7 +4,7 @@
namespace Icinga\Protocol\File;
use FilterIterator;
use Icinga\Util\EnumeratingFilterIterator;
use Icinga\Util\File;
/**
@ -12,7 +12,7 @@ use Icinga\Util\File;
*
* Iterate over a file, yielding only fields of non-empty lines which match a PCRE expression
*/
class FileIterator extends FilterIterator
class FileIterator extends EnumeratingFilterIterator
{
/**
* A PCRE string with the fields to extract from the file's lines as named subpatterns

@ -6,7 +6,6 @@ namespace Icinga\Protocol\File;
use Icinga\Data\Selectable;
use Countable;
use Icinga\Util\Enumerate;
use Zend_Config;
/**
@ -53,9 +52,7 @@ class FileReader implements Selectable, Countable
*/
public function iterate()
{
return new Enumerate(
new FileIterator($this->filename, $this->fields)
);
return new FileIterator($this->filename, $this->fields);
}
/**

@ -1,13 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat\Exception;
/**
* Class ParsingException
* @package Icinga\Protocol\Statusdat\Exception
*/
class ParsingException extends \RuntimeException
{
}

@ -1,20 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
interface IReader
{
/**
* @return mixed
*/
public function getState();
/**
* @param $type
* @param $name
* @return mixed
*/
public function getObjectByName($type, $name);
}

@ -1,51 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
/**
* Class ObjectContainer
* @package Icinga\Protocol\Statusdat
*/
class ObjectContainer extends \stdClass
{
/**
* @var \stdClass
*/
public $ref;
/**
* @var IReader
*/
public $reader;
/**
* @param \stdClass $obj
* @param IReader $reader
*/
public function __construct(&$obj, IReader &$reader)
{
$this->ref = & $obj;
$this->reader = & $reader;
}
/**
* @param $attribute
* @return \stdClass
*/
public function __get($attribute)
{
$exploded = explode(".", $attribute);
$result = $this->ref;
foreach ($exploded as $elem) {
if (isset($result->$elem)) {
$result = $result->$elem;
} else {
return null;
}
}
return $result;
}
}

@ -1,422 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
use Icinga\Util\File;
use Icinga\Exception\ProgrammingError;
use Icinga\Protocol\Statusdat\Exception\ParsingException as ParsingException;
/**
* Status.dat and object.cache parser implementation
*/
class Parser
{
/**
* An array of objects that couldn't be resolved yet due to missing dependencies
*
* @var array
*/
private $deferred = array();
/**
* The currently read file
*
* @var File
*/
private $file;
/**
* String representation of the currently parsed object type
*
* @var string
*/
private $currentObjectType;
/**
* The current state type (host, service)
*
* @var string
*/
private $currentStateType;
/**
* The internal representation of the icinga statue
*
* @var array
*/
private $icingaState;
/**
* The current line being read
*
* @var int
*/
private $lineCtr = 0;
/**
* Create a new parser using the given file
*
* @param File $file The file to parse
* @param array $baseState The state to use for the base
*/
public function __construct(File $file, $baseState = null)
{
$this->file = $file;
$this->icingaState = $baseState;
}
/**
* Parse the given file handle as an objects file and read object information
*/
public function parseObjectsFile()
{
$DEFINE = strlen('define ');
$this->icingaState = array();
foreach ($this->file as $line) {
$line = trim($line);
$this->lineCtr++;
if ($line === '' || $line[0] === '#') {
continue;
}
$this->currentObjectType = trim(substr($line, $DEFINE, -1));
if (!isset($this->icingaState[$this->currentObjectType])) {
$this->icingaState[$this->currentObjectType] = array();
}
$this->readCurrentObject();
}
$this->processDeferred();
}
/**
* Parse the given file as an status.dat file and read runtime information
*
* @param File $file The file to parse or null to parse the one passed to the constructor
*/
public function parseRuntimeState(File $file = null)
{
if ($file != null) {
$this->file = $file;
} else {
$file = $this->file;
}
if (!$this->icingaState) {
throw new ProgrammingError('Tried to read runtime state without existing objects data');
}
$this->overwrites = array();
foreach ($file as $line) {
$line = trim($line);
$this->lineCtr++;
if ($line === '' || $line[0] === '#') {
continue;
}
$this->currentStateType = trim(substr($line, 0, -1));
$this->readCurrentState();
}
}
/**
* Read the next object from the object.cache file handle
*
* @throws ParsingException
*/
private function readCurrentObject()
{
$monitoringObject = new PrintableObject();
foreach ($this->file as $line) {
$line = explode("\t", trim($line), 2);
$this->lineCtr++;
if (!$line) {
continue;
}
// End of object
if ($line[0] === '}') {
$this->registerObject($monitoringObject);
return;
}
if (!isset($line[1])) {
$line[1] = '';
}
$monitoringObject->{$line[0]} = trim($line[1]);
}
throw new ParsingException('Unexpected EOF in objects.cache, line ' . $this->lineCtr);
}
/**
* Read the next state from the status.dat file handler
*
* @throws Exception\ParsingException
*/
private function readCurrentState()
{
$statusdatObject = new RuntimeStateContainer();
$objectType = $this->getObjectTypeForState();
if ($objectType != 'host' && $objectType != 'service') {
$this->skipObject(); // ignore unknown objects
return;
}
if (!isset($this->icingaState[$this->currentObjectType])) {
throw new ParsingException("No $this->currentObjectType objects registered in objects.cache");
}
$base = & $this->icingaState[$this->currentObjectType];
$state = $this->skipObject(true);
$statusdatObject->runtimeState = & $state;
$name = $this->getObjectIdentifier($statusdatObject);
if (!isset($base[$name])) {
throw new ParsingException(
"Unknown object $name " . $this->currentObjectType . ' - '
. print_r(
$statusdatObject,
true
)
. "\n" . print_r($base, true)
);
}
$type = substr($this->currentStateType, strlen($objectType));
if ($type == 'status') {
// directly set the status to the status field of the given object
$base[$name]->status = & $statusdatObject;
} else {
if (!isset($base[$name]->$type) || !in_array($base[$name]->$type, $this->overwrites)) {
$base[$name]->$type = array();
$this->overwrites[] = & $base[$name]->$type;
}
array_push($base[$name]->$type, $statusdatObject);
$this->currentObjectType = $type;
if (!isset($this->icingaState[$type])) {
$this->icingaState[$type] = array();
}
$this->icingaState[$type][] = &$statusdatObject;
$id = $this->getObjectIdentifier($statusdatObject);
if ($id !== false && isset($this->icingaState[$objectType][$id])) {
$statusdatObject->$objectType = $this->icingaState[$objectType][$id];
}
}
return;
}
/**
* Get the corresponding object type name for the given state
*
* @return string
*/
private function getObjectTypeForState()
{
$pos = strpos($this->currentStateType, 'service');
if ($pos === false) {
$pos = strpos($this->currentStateType, 'host');
} else {
$this->currentObjectType = 'service';
return 'service';
}
if ($pos === false) {
return $this->currentStateType;
} else {
$this->currentObjectType = 'host';
return 'host';
}
return $this->currentObjectType;
}
/**
* Skip the current object definition
*
* @param bool $returnString If true, the object string will be returned
* @return string The skipped object if $returnString is true
*/
protected function skipObject($returnString = false)
{
if (!$returnString) {
while (trim($this->file->fgets()) !== '}') {
}
return null;
} else {
$str = '';
while (($val = trim($this->file->fgets())) !== '}') {
$str .= $val . "\n";
}
return $str;
}
}
/**
* Register the given object in the icinga state
*
* @param object $object The monitoring object to register
*/
protected function registerObject(&$object)
{
$name = $this->getObjectIdentifier($object);
if ($name !== false) {
$this->icingaState[$this->currentObjectType][$name] = &$object;
}
$this->registerObjectAsProperty($object);
}
/**
* Register the given object as a property in related objects
*
* This registers for example hosts underneath their hostgroup and vice cersa
*
* @param object $object The object to register as a property
*/
protected function registerObjectAsProperty(&$object)
{
if ($this->currentObjectType == 'service'
|| $this->currentObjectType == 'host'
|| $this->currentObjectType == 'contact') {
return null;
}
$isService = strpos($this->currentObjectType, 'service') !== false;
$isHost = strpos($this->currentObjectType, 'host') !== false;
$isContact = strpos($this->currentObjectType, 'contact') !== false;
$name = $this->getObjectIdentifier($object);
if ($isService === false && $isHost === false && $isContact === false) {
// this would be error in the parser implementation
return null;
}
$property = $this->currentObjectType;
if ($isService) {
$this->currentObjectType = 'service';
$property = substr($property, strlen('service'));
} elseif ($isHost) {
$this->currentObjectType = 'host';
$property = substr($property, strlen('host'));
} elseif ($isContact) {
$this->currentObjectType = 'contact';
$property = substr($property, strlen('contact'));
}
if (!isset($this->icingaState[$this->currentObjectType])) {
return $this->deferRegistration($object, $this->currentObjectType . $property);
}
// @TODO: Clean up, this differates between 1:n and 1:1 references
if (strpos($property, 'group') !== false) {
$sourceIdentifier = $this->getMembers($object);
foreach ($sourceIdentifier as $id) {
$source = $this->icingaState[$this->currentObjectType][$id];
if (!isset($source->$property)) {
$source->$property = array();
}
$type = $this->currentObjectType;
if (!isset($object->$type)) {
$object->$type = array();
}
// add the member to the group object
array_push($object->$type, $source);
// add the group to the member object
array_push($source->$property, $name);
}
} else {
$source = $this->icingaState[$this->currentObjectType][$this->getObjectIdentifier($object)];
if (!isset($source->$property)) {
$source->$property = array();
}
array_push($source->$property, $object);
}
return null;
}
/**
* Defer registration of the given object
*
* @param object $object The object to defer
* @param String $objType The name of the object type
*/
protected function deferRegistration($object, $objType)
{
$this->deferred[] = array($object, $objType);
}
/**
* Process deferred objects
*/
protected function processDeferred()
{
foreach ($this->deferred as $obj) {
$this->currentObjectType = $obj[1];
$this->registerObjectAsProperty($obj[0]);
}
}
/**
* Return the resolved members directive of an object
*
* @param object $object The object to get the members from
* @return array An array of member names
*/
protected function getMembers(&$object)
{
if (!isset($object->members)) {
return array();
}
$members = explode(',', $object->members);
if ($this->currentObjectType == 'service') {
$res = array();
for ($i = 0; $i < count($members); $i += 2) {
$res[] = $members[$i] . ';' . $members[$i + 1];
}
return $res;
} else {
return $members;
}
}
/**
* Return the unique name of the given object
*
* @param object $object The object to retrieve the name from
* @return string The name of the object or null if no name can be retrieved
*/
protected function getObjectIdentifier(&$object)
{
if ($this->currentObjectType == 'contact') {
return $object->contact_name;
}
if ($this->currentObjectType == 'service') {
return $object->host_name . ';' . $object->service_description;
}
$name = $this->currentObjectType . '_name';
if (isset($object->{$name})) {
return $object->{$name};
}
if (isset($object->service_description)) {
return $object->host_name . ';' . $object->service_description;
} elseif (isset($object->host_name)) {
return $object->host_name;
}
return null;
}
/**
* Return the internal state of the parser
*
* @return null
*/
public function getRuntimeState()
{
return $this->icingaState;
}
}

@ -1,20 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
class PrintableObject
{
public function __toString()
{
if (isset($this->contact_name)) {
return $this->contact_name;
} elseif (isset($this->service_description)) {
return $this->service_description;
} elseif (isset($this->host_name)) {
return $this->host_name;
}
return '';
}
}

@ -1,460 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
use Exception;
use Icinga\Exception\ProgrammingError;
use Icinga\Data\SimpleQuery;
use Icinga\Protocol\Statusdat\View\MonitoringObjectList;
use Icinga\Protocol\Statusdat\Query\IQueryPart;
use Icinga\Exception\IcingaException;
/**
* Base implementation for Statusdat queries.
*/
class Query extends SimpleQuery
{
/**
* An array denoting valid targets by mapping the query target to
* the 'define' directives found in the status.dat/objects.cache files
*
* @var array
*/
public static $VALID_TARGETS = array(
'hosts' => array('host'),
'services' => array('service'),
'downtimes' => array('downtime'),
'groups' => array('hostgroup', 'servicegroup'),
'hostgroups' => array('hostgroup'),
'servicegroups' => array('servicegroup'),
'comments' => array('comment'),
'contacts' => array('contact'),
'contactgroups' => array('contactgroup')
);
/**
* The current StatusDat query that will be applied upon calling fetchAll
*
* @var IQueryPart
*/
private $queryFilter = null;
/**
* The current query source being used
*
* @var string
*/
private $source = '';
/**
* An array containing all columns used for sorting
*
* @var array
*/
protected $orderColumns = array();
/**
* An array containig all columns used for (simple) grouping
*
* @var array
*/
private $groupColumns = array();
/**
* An optional function callback to use for more specific grouping
*
* @var array
*/
private $groupByFn = null;
/**
* The scope index for the callback function
*/
const FN_SCOPE = 0;
/**
* The name index for the callback function
*/
const FN_NAME = 1;
/**
* Return true if columns are set for this query
*
* @return bool
*/
public function hasColumns()
{
$columns = $this->getColumns();
return !empty($columns);
}
/**
* Set the status.dat specific IQueryPart filter to use
*
* @param IQueryPart $filter
*/
public function setQueryFilter($filter)
{
$this->queryFilter = $filter;
}
/**
* Order the query result by the given columns
*
* @param String|array $columns An array of columns to order by
* @param String $dir The direction (asc or desc) in string form
*
* @return $this Fluent interface
*/
public function order($columns, $dir = null, $isFunction = false)
{
if ($dir && strtolower($dir) == 'desc') {
$dir = self::SORT_DESC;
} else {
$dir = self::SORT_ASC;
}
if (!is_array($columns)) {
$columns = array($columns);
}
foreach ($columns as $col) {
if (($pos = strpos($col, ' ')) !== false) {
$dir = strtoupper(substr($col, $pos + 1));
if ($dir === 'DESC') {
$dir = self::SORT_DESC;
} else {
$dir = self::SORT_ASC;
}
$col = substr($col, 0, $pos);
} else {
$col = $col;
}
$this->orderColumns[] = array($col, $dir);
}
return $this;
}
/**
* Order the query result using the callback to retrieve values for items
*
* @param array $columns A scope, function array to use for retrieving the values when ordering
* @param String $dir The direction (asc or desc) in string form
*
* @return $this Fluent interface
*/
public function orderByFn(array $callBack, $dir = null)
{
if ($dir && strtolower($dir) == 'desc') {
$dir = self::SORT_DESC;
} else {
$dir = self::SORT_ASC;
}
$this->orderColumns[] = array($callBack, $dir);
}
/**
* Set the query target
*
* @param String $table The table/target to select the query from
* @param array $columns An array of attributes to use (required for fetchPairs())
*
* @return $this Fluent interface
* @throws IcingaException If the target is unknonw
*/
public function from($table, array $attributes = null)
{
if (!$this->getColumns() && $attributes) {
$this->setColumns($attributes);
}
if (isset(self::$VALID_TARGETS[$table])) {
$this->source = $table;
} else {
throw new IcingaException(
'Unknown from target for status.dat :%s',
$table
);
}
return $this;
}
/**
* Return an index of all objects matching the filter of this query
*
* This index will be used for ordering, grouping and limiting
*/
private function getFilteredIndices($classType = '\Icinga\Protocol\Statusdat\Query\Group')
{
$baseGroup = $this->queryFilter;
$state = $this->ds->getState();
$result = array();
$source = self::$VALID_TARGETS[$this->source];
foreach ($source as $target) {
if (! isset($state[$target])) {
continue;
}
$indexes = array_keys($state[$target]);
if ($baseGroup) {
$baseGroup->setQuery($this);
$idx = array_keys($state[$target]);
$indexes = $baseGroup->filter($state[$target], $idx);
}
if (!isset($result[$target])) {
$result[$target] = $indexes;
} else {
array_merge($result[$target], $indexes);
}
}
return $result;
}
/**
* Order the given result set
*
* @param array $indices The result set of the query that should be ordered
*/
private function orderIndices(array &$indices)
{
if (!empty($this->orderColumns)) {
foreach ($indices as $type => &$subindices) {
$this->currentType = $type;
usort($subindices, array($this, 'orderResult'));
}
}
}
/**
* Start a query
*
* This is just a dummy function to allow a more convenient syntax
*
* @return self Fluent interface
*/
public function select()
{
return $this;
}
/**
* Order implementation called by usort
*
* @param String $a The left object index
* @param Strinv $b The right object index
* @return int 0, 1 or -1, see usort for detail
*/
private function orderResult($a, $b)
{
$o1 = $this->ds->getObjectByName($this->currentType, $a);
$o2 = $this->ds->getObjectByName($this->currentType, $b);
$result = 0;
foreach ($this->orderColumns as &$col) {
if (is_array($col[0])) {
// sort by function
$result += $col[1] * strnatcasecmp(
$col[0][0]->$col[0][1]($o1),
$col[0][0]->$col[0][1]($o2)
);
} else {
$result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]});
}
}
return $result;
}
/**
* Limit the given resultset
*
* @param array $indices The filtered, ordered indices
*/
private function limitIndices(array &$indices)
{
foreach ($indices as $type => $subindices) {
$indices[$type] = array_slice($subindices, $this->getOffset(), $this->getLimit());
}
}
/**
* Register the given function for grouping the result
*
* @param String $fn The function to use for grouping
* @param Object $scope An optional scope to use instead of $this
*
* @return self Fluent interface
*/
public function groupByFunction($fn, $scope = null)
{
$this->groupByFn = array($scope ? $scope : $this, $fn);
return $this;
}
/**
* Group by the given column
*
* @param array|string $columns The columns to use for grouping
* @return self Fluent interface
* @see Query::columnGroupFn() The implementation used for grouping
*/
public function groupByColumns($columns)
{
if (!is_array($columns)) {
$columns = array($columns);
}
$this->groupColumns = $columns;
$this->groupByFn = array($this, 'columnGroupFn');
return $this;
}
/**
* The internal handler function used by the group function
*
* @param array $indices The indices to group
* @return array The grouped result set
*/
private function columnGroupFn(array &$indices)
{
$cols = $this->groupColumns;
$result = array();
foreach ($indices as $type => $subindices) {
foreach ($subindices as $objectIndex) {
$r = $this->ds->getObjectByName($type, $objectIndex);
$hash = '';
$cols = array();
foreach ($this->groupColumns as $col) {
$hash = md5($hash . $r->$col);
$cols[$col] = $r->$col;
}
if (!isset($result[$hash])) {
$result[$hash] = (object)array(
'columns' => (object)$cols,
'count' => 0
);
}
$result[$hash]->count++;
}
}
return array_values($result);
}
/**
* Query Filter, Order, Group, Limit and return the result set
*
* @return array The resultset matching this query
*/
public function getResult()
{
$indices = $this->getFilteredIndices();
$this->orderIndices($indices);
if ($this->groupByFn) {
$scope = $this->groupByFn[self::FN_SCOPE];
$fn = $this->groupByFn[self::FN_NAME];
return $scope->$fn($indices);
}
$this->limitIndices($indices);
$result = array();
$state = $this->ds->getState();
foreach ($indices as $type => $subindices) {
foreach ($subindices as $index) {
$result[] = & $state[$type][$index];
}
}
return $result;
}
/**
* Apply all filters of this filterable on the datasource
*/
public function applyFilter()
{
$parser = new TreeToStatusdatQueryParser();
if ($this->getFilter()) {
$query = $parser->treeToQuery($this->getFilter(), $this);
$this->setQueryFilter($query);
}
}
/**
* Return only the first row fetched from the result set
*
* @return MonitoringObjectList The monitoring object matching this query
*/
public function fetchRow()
{
$rs = $this->fetchAll();
$rs->rewind();
return $rs->current();
}
/**
* Fetch the result as an associative array using the first column as the key and the second as the value
*
* @return array An associative array with the result
* @throws IcingaException If no attributes are defined
*/
public function fetchPairs()
{
$result = array();
if (count($this->getColumns()) < 2) {
throw new IcingaException(
'Status.dat "fetchPairs()" query expects at least columns to be set in the query expression'
);
}
$attributes = $this->getColumns();
$param1 = $attributes[0];
$param2 = $attributes[1];
foreach ($this->fetchAll() as $resultList) {
$result[$resultList->$param1] = $resultList->$param2;
}
return $result;
}
/**
* Fetch all results
*
* @return MonitoringObjectList An MonitoringObjectList wrapping the given resultset
*/
public function fetchAll()
{
$this->applyFilter();
if (!isset($this->cursor)) {
$result = $this->getResult();
$this->cursor = new MonitoringObjectList($result, $this);
}
return $this->cursor;
}
/**
* Return the value of the first column for the first row fetched from the result set
*/
public function fetchOne()
{
throw new ProgrammingError('Statusdat/Query::fetchOne() is not implemented yet');
}
/**
* Count the number of results
*
* @return int
*/
public function count()
{
$q = clone $this;
$q->limit(null, null);
return count($q->fetchAll());
}
}

@ -1,415 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat\Query;
use Icinga\Exception\IcingaException;
class Expression implements IQueryPart
{
/**
*
*/
const ENC_NUMERIC = 0;
/**
*
*/
const ENC_SET = 0;
/**
*
*/
const ENC_STRING = 0;
/**
* @var string
*/
private $expression;
/**
* @var null
*/
private $field = null;
/**
* @var array
*/
private $basedata = array();
/**
* @var null
*/
private $function = null;
/**
* @var string
*/
private $value = "";
/**
* @var null
*/
private $operator = null;
/**
* Optional query information
*
* @var null
*/
private $query = null;
/**
* @var null
*/
private $name = null;
/**
* @var null
*/
public $CB = null;
/**
* @param $token
* @throws IcingaException
*/
private function getOperatorType($token)
{
switch (strtoupper($token)) {
case ">":
$this->CB = "isGreater";
break;
case "<":
$this->CB = "isLess";
break;
case ">=":
$this->CB = "isGreaterEq";
break;
case "<=":
$this->CB = "isLessEq";
break;
case "=":
$this->CB = "isEqual";
break;
case "LIKE":
$this->CB = "isLike";
break;
case "NOT_LIKE":
$this->CB = "isNotLike";
break;
case "!=":
$this->CB = "isNotEqual";
break;
case "IN":
$this->CB = "isIn";
break;
case "NOT_IN":
$this->CB = "isNotIn";
break;
default:
throw new IcingaException(
'Unknown operator %s in expression %s !',
$token,
$this->expression
);
}
}
/**
* @param $tokens
* @return mixed
*/
private function extractAggregationFunction(&$tokens)
{
$token = $tokens[0];
$value = array();
if (preg_match("/COUNT\{(.*)\}/", $token, $value) == false) {
return $token;
}
$this->function = "count";
$tokens[0] = $value[1];
return null;
}
/**
* @param $values
*/
private function parseExpression(&$values)
{
$tokenized = preg_split("/ +/", trim($this->expression), 3);
$this->extractAggregationFunction($tokenized);
if (count($tokenized) != 3) {
echo(
"Currently statusdat query expressions must be in "
. "the format FIELD OPERATOR ? or FIELD OPERATOR :value_name"
);
}
$this->fields = explode(".", trim($tokenized[0]));
$this->field = $this->fields[count($this->fields) - 1];
$this->getOperatorType(trim($tokenized[1]));
$tokenized[2] = trim($tokenized[2]);
if ($tokenized[2][0] === ":") {
$this->name = substr($tokenized, 1);
$this->value = $values[$this->name];
} else {
if ($tokenized[2] === "?") {
$this->value = array_shift($values);
} else {
$this->value = trim($tokenized[2]);
}
}
}
/**
* @param $expression
* @param $values
* @return $this
*/
public function fromString($expression, &$values)
{
$this->expression = $expression;
$this->parseExpression($values);
return $this;
}
/**
* @param null $expression
* @param array $values
*/
public function __construct($expression = null, &$values = array())
{
if ($expression) {
if (!is_array($values)) {
$values = array($values);
}
$this->fromString($expression, $values);
}
}
/**
* @param array $base
* @param array $idx
* @return array|mixed
*/
public function filter(array &$base, &$idx = array())
{
if (!$idx) {
$idx = array_keys($base);
}
$this->basedata = $base;
return array_filter($idx, array($this, "filterFn"));
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @return null
*/
public function getField()
{
return $this->field;
}
/**
* @param $idx
* @return bool
*/
protected function filterFn($idx)
{
$values = $this->getFieldValues($idx);
if ($values === false) {
return false;
}
if ($this->CB == "isIn" || $this->CB == "isNotIn") {
$cmpValues = is_array($this->value) ? $this->value : array($this->value);
foreach ($cmpValues as $cmpValue) {
$this->value = $cmpValue;
foreach ($values as $value) {
if ($this->CB == "isIn" && $this->isLike($value)) {
$this->value = $cmpValues;
return true;
} elseif ($this->CB == "isNotIn" && $this->isNotLike($value)) {
$this->value = $cmpValues;
return true;
}
}
}
$this->value = $cmpValues;
return false;
}
if ($this->function) {
$values = call_user_func($this->function, $values);
if (!is_array($values)) {
$values = array($values);
}
}
foreach ($values as $val) {
if (!is_string($val) && !is_numeric($val) && is_object($val)) {
if (isset($val->service_description)) {
$val = $val->service_description;
} elseif (isset($val->host_name)) {
$val = $val->host_name;
} else {
return false;
}
}
if ($this->{$this->CB}($val)) {
return true;
}
}
return false;
}
/**
* @param $idx
* @return array
*/
private function getFieldValues($idx)
{
$res = $this->basedata[$idx];
foreach ($this->fields as $field) {
if (!is_array($res)) {
if ($this->query) {
$res = $this->query->get($res, $field);
continue;
}
if (!isset($res->$field)) {
$res = array();
break;
}
$res = $res->$field;
continue;
}
// it can be that an element contains more than one value, like it
// happens when using comments, in this case we have to create a new
// array that contains the values/objects we're searching
$swap = array();
foreach ($res as $sub) {
if ($this->query) {
$swap[] = $this->query->get($sub, $field);
continue;
}
if (!isset($sub->$field)) {
continue;
}
if (!is_array($sub->$field)) {
$swap[] = $sub->$field;
} else {
$swap = array_merge($swap, $sub->$field);
}
}
$res = $swap;
}
if (!is_array($res)) {
return array($res);
}
return $res;
}
/**
* @param $value
* @return bool
*/
public function isGreater($value)
{
return $value > $this->value;
}
/**
* @param $value
* @return bool
*/
public function isLess($value)
{
return $value < $this->value;
}
/**
* @param $value
* @return bool
*/
public function isLike($value)
{
return preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false;
}
/**
* @param $value
* @return bool
*/
public function isNotLike($value)
{
return !preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false;
}
/**
* @param $value
* @return bool
*/
public function isEqual($value)
{
if (!is_numeric($value)) {
return strtolower($value) == strtolower($this->value);
}
return $value == $this->value;
}
/**
* @param $value
* @return bool
*/
public function isNotEqual($value)
{
return $value != $this->value;
}
/**
* @param $value
* @return bool
*/
public function isGreaterEq($value)
{
return $value >= $this->value;
}
/**
* @param $value
* @return bool
*/
public function isLessEq($value)
{
return $value <= $this->value;
}
/**
* Add additional information about the query this filter belongs to
*
* @param $query
* @return mixed
*/
public function setQuery($query)
{
$this->query = $query;
}
}

@ -1,397 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat\Query;
use Icinga\Exception\IcingaException;
/**
* Class Group
* @package Icinga\Protocol\Statusdat\Query
*/
class Group implements IQueryPart
{
/**
*
*/
const GROUP_BEGIN = "(";
/**
*
*/
const GROUP_END = ")";
/**
*
*/
const CONJUNCTION_AND = "AND ";
/**
*
*/
const CONJUNCTION_OR = "OR ";
/**
*
*/
const EXPRESSION = 0;
/**
*
*/
const EOF = -1;
/**
*
*/
const TYPE_AND = "AND";
/**
*
*/
const TYPE_OR = "OR";
/**
* @var array
*/
private $items = array();
/**
* @var int
*/
private $parsePos = 0;
/**
* @var string
*/
private $expression = "";
/**
* @var null
*/
private $expressionClass = null;
/**
* @var string
*/
private $type = "";
/**
* @var int
*/
private $subExpressionStart = 0;
/**
* @var int
*/
private $subExpressionLength = 0;
/**
* Optional query to use
*
* @var Query
*/
private $query = null;
/**
* @var
*/
private $value;
/**
* @param $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* @return array
*/
public function getItems()
{
return $this->items;
}
/**
* @return string
*/
public function getType()
{
return $this->type ? $this->type : self::TYPE_AND;
}
/**
* @param $type
*/
public function setType($type)
{
$this->type = $type;
}
/**
* @throws IcingaException
*/
private function tokenize()
{
$token = 0;
$subgroupCount = 0;
while ($token != self::EOF) {
$token = $this->getNextToken();
if ($token === self::GROUP_BEGIN) {
/**
* check if this is a nested group, if so then it's
* considered part of the subexpression
*/
if ($subgroupCount == 0) {
$this->startNewSubExpression();
}
$subgroupCount++;
continue;
}
if ($token === self::GROUP_END) {
if ($subgroupCount < 1) {
throw new IcingaException(
'Invalid Query: unexpected \')\' at pos %s',
$this->parsePos
);
}
$subgroupCount--;
/*
* check if this is a nested group, if so then it's
* considered part of the subexpression
*/
if ($subgroupCount == 0) {
$this->addSubgroupFromExpression();
}
continue;
}
if ($token === self::CONJUNCTION_AND && $subgroupCount == 0) {
$this->startNewSubExpression();
if ($this->type != self::TYPE_AND && $this->type != "") {
$this->createImplicitGroup(self::TYPE_AND);
break;
} else {
$this->type = self::TYPE_AND;
}
continue;
}
if ($token === self::CONJUNCTION_OR && $subgroupCount == 0) {
$this->startNewSubExpression();
if ($this->type != self::TYPE_OR && $this->type != "") {
$this->createImplicitGroup(self::TYPE_OR);
break;
} else {
$this->type = self::TYPE_OR;
}
continue;
}
$this->subExpressionLength = $this->parsePos - $this->subExpressionStart;
}
if ($subgroupCount > 0) {
throw new IcingaException('Unexpected end of query, are you missing a parenthesis?');
}
$this->startNewSubExpression();
}
/**
* @param $type
*/
private function createImplicitGroup($type)
{
$group = new Group();
$group->setType($type);
$group->addItem(array_pop($this->items));
$group->fromString(substr($this->expression, $this->parsePos), $this->value, $this->expressionClass);
$this->items[] = $group;
$this->parsePos = strlen($this->expression);
}
/**
*
*/
private function startNewSubExpression()
{
if ($this->getCurrentSubExpression() != "") {
if (!$this->expressionClass) {
$this->items[] = new Expression($this->getCurrentSubExpression(), $this->value);
} else {
$this->items[] = new $this->expressionClass($this->getCurrentSubExpression(), $this->value);
}
}
$this->subExpressionStart = $this->parsePos;
$this->subExpressionLength = 0;
}
/**
* @return string
*/
private function getCurrentSubExpression()
{
return substr($this->expression, $this->subExpressionStart, $this->subExpressionLength);
}
/**
*
*/
private function addSubgroupFromExpression()
{
if (!$this->expressionClass) {
$this->items[] = new Group($this->getCurrentSubExpression(), $this->value);
} else {
$group = new Group();
$group->fromString($this->getCurrentSubExpression(), $this->value, $this->expressionClass);
$this->items[] = $group;
}
$this->subExpressionStart = $this->parsePos;
$this->subExpressionLength = 0;
}
/**
* @return bool
*/
private function isEOF()
{
if ($this->parsePos >= strlen($this->expression)) {
return true;
}
return false;
}
/**
* @return int|string
*/
private function getNextToken()
{
if ($this->isEOF()) {
return self::EOF;
}
// skip whitespaces
while ($this->expression[$this->parsePos] == " ") {
$this->parsePos++;
if ($this->isEOF()) {
return self::EOF;
}
}
if ($this->expression[$this->parsePos] == self::GROUP_BEGIN) {
$this->parsePos++;
return self::GROUP_BEGIN;
}
if ($this->expression[$this->parsePos] == self::GROUP_END) {
$this->parsePos++;
return self::GROUP_END;
}
if (substr_compare(
$this->expression,
self::CONJUNCTION_AND,
$this->parsePos,
strlen(self::CONJUNCTION_AND),
true
) === 0) {
$this->parsePos += strlen(self::CONJUNCTION_AND);
return self::CONJUNCTION_AND;
}
if (substr_compare(
$this->expression,
self::CONJUNCTION_OR,
$this->parsePos,
strlen(self::CONJUNCTION_OR),
true
) === 0) {
$this->parsePos += strlen(self::CONJUNCTION_OR);
return self::CONJUNCTION_OR;
}
$this->parsePos++;
return self::EXPRESSION;
}
/**
* @param $ex
* @return $this
*/
public function addItem($ex)
{
$this->items[] = $ex;
return $this;
}
/**
* @param $expression
* @param array $value
* @param null $expressionClass
* @return $this
*/
public function fromString($expression, &$value = array(), $expressionClass = null)
{
$this->expression = $expression;
$this->value = & $value;
$this->expressionClass = $expressionClass;
$this->tokenize();
return $this;
}
/**
* @param null $expression
* @param array $value
*/
public function __construct($expression = null, &$value = array())
{
if ($expression) {
$this->fromString($expression, $value);
}
}
/**
* @param array $base
* @param null $idx
* @return array|null
*/
public function filter(array &$base, &$idx = null)
{
if ($this->type == self::TYPE_OR) {
$idx = array();
foreach ($this->items as &$subFilter) {
$baseKeys = array_keys($base);
$subFilter->setQuery($this->query);
$idx += $subFilter->filter($base, $baseKeys);
}
} else {
if (!$idx) {
$idx = array_keys($base);
}
foreach ($this->items as $subFilter) {
$subFilter->setQuery($this->query);
$idx = array_intersect($idx, $subFilter->filter($base, $idx));
}
}
return $idx;
}
/**
* Add additional information about the query this filter belongs to
*
* @param $query
* @return mixed
*/
public function setQuery($query)
{
$this->query = $query;
}
}

@ -1,36 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat\Query;
/**
* Class IQueryPart
* @package Icinga\Protocol\Statusdat\Query
*/
interface IQueryPart
{
/**
* Create a new query part with an optional expression to be parse
*
* @param string $expression An optional expression string to use
* @param array $value The values fot the optional expression
*/
public function __construct($expression = null, &$value = array());
/**
* Filter the given resultset
*
* @param array $base The resultset to use for filtering
* @param array $idx An optional array containing prefiltered indices
*/
public function filter(array &$base, &$idx = null);
/**
* Add additional information about the query this filter belongs to
*
* @param $query
* @return mixed
*/
public function setQuery($query);
}

@ -1,330 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
use Icinga\Util\File;
use Icinga\Logger\Logger;
use Icinga\Data\Selectable;
use Icinga\Exception\ConfigurationError;
/**
* Class Reader
* @package Icinga\Protocol\Statusdat
*/
class Reader implements IReader, Selectable
{
/**
* The default lifetime of the cache in milliseconds
*/
const DEFAULT_CACHE_LIFETIME = 30;
/**
* The folder for the statusdat cache
*/
const STATUSDAT_DEFAULT_CACHE_PATH = '/tmp';
/**
* The last state from the cache
*
* @var array
*/
private $lastState;
/**
* True when this reader has already acquired the current runtime state (i.e. Status.dat)
*
* @var bool
*/
private $hasRuntimeState = false;
/**
* The representation of the object.cache file
*
* @var array
*/
private $objectCache ;
/**
* The representation of the status.dat file
* @var array
*/
private $statusCache;
/**
* True when the icinga state differs from the cache
*
* @var bool
*/
private $newState = false;
/**
* The Parser object to use for parsing
*
* @var Parser
*/
private $parser;
/**
* Whether to disable the cache
*
* @var bool
*/
private $noCache;
/**
* Create a new Reader from the given configuraion
*
* @param Zend_Config $config The configuration to read the status.dat information from
* @param Parser $parser The parser to use (for testing)
* @param bool $noCache Whether to disable the cache
*/
public function __construct($config = \Zend_Config, $parser = null, $noCache = false)
{
$this->noCache = $noCache;
if (isset($config->no_cache)) {
$this->noCache = $config->no_cache;
}
$this->config = $config;
$this->parser = $parser;
if (!$this->noCache) {
$this->cache = $this->initializeCaches($config);
if ($this->fromCache()) {
$this->createHostServiceConnections();
return;
}
}
if (!$this->lastState) {
$this->parseObjectsCacheFile();
}
if (!$this->hasRuntimeState) {
}
$this->parseStatusDatFile();
if (!$noCache && $this->newState) {
$this->statusCache->save($this->parser->getRuntimeState(), 'object' . md5($this->config->object_file));
}
$this->createHostServiceConnections();
}
/**
* Initialize the internal caches if enabled
*
* @throws ConfigurationError
*/
private function initializeCaches()
{
$defaultCachePath = self::STATUSDAT_DEFAULT_CACHE_PATH;
$cachePath = $this->config->get('cache_path', $defaultCachePath);
$maxCacheLifetime = intval($this->config->get('cache_path', self::DEFAULT_CACHE_LIFETIME));
$cachingEnabled = true;
if (!is_writeable($cachePath)) {
Logger::warning(
'Can\'t cache Status.dat backend; make sure cachepath %s is writable by the web user. '
. 'Caching is now disabled',
$cachePath
);
$cachePath = null;
}
$backendOptions = array(
'cache_dir' => $cachePath
);
// the object cache might exist for months and is still valid
$this->objectCache = $this->initCache($this->config->object_file, $backendOptions, null, $cachingEnabled);
$this->statusCache = $this->initCache(
$this->config->status_file,
$backendOptions,
$maxCacheLifetime,
$cachingEnabled
);
}
/**
* Init the Cache backend in Zend
*
* @param String $file The file to use as the cache master file
* @param Zend_Config $backend The backend configuration to use
* @param integer $lifetime The lifetime of the cache
*
* @return \Zend_Cache_Core|\Zend_Cache_Frontend
*/
private function initCache($file, $backendConfig, $lifetime)
{
$frontendOptions = array(
'lifetime' => $lifetime,
'automatic_serialization' => true,
'master_files' => array($file)
);
return \Zend_Cache::factory('Core', 'File', $frontendOptions, $backendConfig);
}
/**
* Read the current cache state
*
* @return bool True if the state is the same as the icinga state
*/
private function fromCache()
{
if (!$this->readObjectsCache()) {
$this->newState = true;
return false;
}
if (!$this->readStatusCache()) {
$this->newState = true;
return false;
}
return true;
}
/**
* Read the object.cache file from the Zend_Cache backend
*
* @return bool True if the file could be loaded from cache
*/
private function readObjectsCache()
{
$this->lastState = $this->objectCache->load('object' . md5($this->config->object_file));
if ($this->lastState == false) {
return false;
}
return true;
}
/**
* Read the status.dat file from the Zend_Cache backend
*
* @return bool True if the file could be loaded from cache
*/
private function readStatusCache()
{
if (!isset($this->stateCache)) {
return true;
}
$statusInfo = $this->stateCache->load('state' . md5($this->config->status_file));
if ($statusInfo == false) {
return false;
}
$this->hasRuntimeState = true;
return true;
}
/**
* Take the status.dat and objects.cache and connect all services to hosts
*
*/
private function createHostServiceConnections()
{
if (!isset($this->lastState["service"])) {
return;
}
foreach ($this->lastState["host"] as &$host) {
$host->host = $host;
}
foreach ($this->lastState["service"] as &$service) {
$service->service = &$service; // allow easier querying
$host = &$this->lastState["host"][$service->host_name];
if (!isset($host->services)) {
$host->services = array();
}
$host->services[$service->service_description] = & $service;
$service->host = & $host;
}
}
/**
* Parse the object.cache file and update the current state
*
* @throws ConfigurationError If the object.cache couldn't be read
*/
private function parseObjectsCacheFile()
{
if (!is_readable($this->config->object_file)) {
throw new ConfigurationError(
'Can\'t read object-file "%s", check your configuration',
$this->config->object_file
);
}
if (!$this->parser) {
$this->parser = new Parser(new File($this->config->object_file, 'r'));
}
$this->parser->parseObjectsFile();
$this->lastState = $this->parser->getRuntimeState();
}
/**
* Parse the status.dat file and update the current state
*
* @throws ConfigurationError If the status.dat couldn't be read
*/
private function parseStatusDatFile()
{
if (!is_readable($this->config->status_file)) {
throw new ConfigurationError(
'Can\'t read status-file %s, check your configuration',
$this->config->status_file
);
}
if (!$this->parser) {
$this->parser = new Parser(new File($this->config->status_file, 'r'), $this->lastState);
}
$this->parser->parseRuntimeState(new File($this->config->status_file, 'r'));
$this->lastState = $this->parser->getRuntimeState();
if (!$this->noCache) {
$this->statusCache->save(array("true" => true), "state" . md5($this->config->object_file));
}
}
/**
* Create a new Query
*
* @return Query The query to operate on
*/
public function select()
{
return new Query($this);
}
/**
* Return the internal state of the status.dat
*
* @return mixed The internal status.dat representation
*/
public function getState()
{
return $this->lastState;
}
/**
* Return the object with the given name and type
*
* @param String $type The type of the object to return (service, host, servicegroup...)
* @param String $name The name of the object
*
* @return ObjectContainer An object container wrapping the result or null if the object doesn't exist
*/
public function getObjectByName($type, $name)
{
if (isset($this->lastState[$type]) && isset($this->lastState[$type][$name])) {
return new ObjectContainer($this->lastState[$type][$name], $this);
}
return null;
}
/**
* Get an array containing all names of monitoring objects with the given type
*
* @param String $type The type of object to get the names for
* @return array An array of names or null if the type does not exist
*/
public function getObjectNames($type)
{
return isset($this->lastState[$type]) ? array_keys($this->lastState[$type]) : null;
}
}

@ -1,72 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
/**
* Container class containing the runtime state of an object
*
* This class contains the state of the object as a string and parses it
* on the fly as soon as values should be retrieved. This reduces memory usage,
* as most runtime information is never received and only lives for a very short time.
*
*/
class RuntimeStateContainer extends \stdClass
{
/**
* The state string
*
* @var string
*/
public $runtimeState = "";
/**
* Create a new runtime state container from the givven string
*
* @param string $str
*/
public function __construct($str = "")
{
$this->runtimeState = $str;
}
/**
* Return true if the argument exists
*
* @param String $attr The argument to retrieve
* @return bool True if it exists, otherwise false
*/
public function __isset($attr)
{
try {
$this->__get($attr);
return true;
} catch (\InvalidArgumentException $e) {
return false;
}
}
/**
* Return the given attribute
*
* If the container string is not yet parsed, this will happen here
*
* @param String $attr The attribute to retrieve
* @return mixed The value of the attribute
* @throws \InvalidArgumentException When the attribute does not exist
*/
public function __get($attr)
{
$start = strpos($this->runtimeState, $attr . "=");
if ($start === false) {
throw new \InvalidArgumentException("Unknown property $attr");
}
$start += strlen($attr . "=");
$len = strpos($this->runtimeState, "\n", $start) - $start;
$this->$attr = trim(substr($this->runtimeState, $start, $len));
return $this->$attr;
}
}

@ -1,42 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat\View;
/**
* Interface for statusdat classes that provide a specific view on the dataset
*
* Views define special get and exists operations for fields that are not directly available
* in a resultset, but exist under another name or can be accessed by loading an additional object
* during runtime.
*
* @see Icinga\Backend\DataView\ObjectRemappingView For an implementation of mapping field names
* to storage specific names, e.g. service_state being status.current_state in status.dat views.
*
* @see Icinga\Backend\MonitoringObjectList For the typical usage of this class. It is not wrapped
* around the monitoring object, so we don't use __get() or __set() and always have to give the
* item we'd like to access.
*/
interface AccessorStrategy
{
/**
* Returns a field for the item, or throws an Exception if the field doesn't exist
*
* @param $item The item to access
* @param $field The field of the item that should be accessed
* @return string The content of the field
*
* @throws \InvalidArgumentException when the field does not exist
*/
public function get(&$item, $field);
/**
* Returns true if the field exists on the specific item, otherwise false
*
* @param $item The item to access
* @param $field The field to check on the $item
* @return bool True when the field exists, otherwise false
*/
public function exists(&$item, $field);
}

@ -1,143 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat\View;
use Iterator;
use Countable;
use ArrayAccess;
use Exception;
use Icinga\Exception\IcingaException;
/**
* Wrapper around an array of monitoring objects that can be enhanced with an optional
* object that extends AccessorStrategy. This will act as a dataview and provide
* normalized access to the underlying data (mapping properties, retrieving additional data)
*
* If not Accessor is set, this class just behaves like a normal Iterator and returns
* the underlying objects.
*
* If the dataset contains arrays instead of objects, they will be cast to objects.
*
*/
class MonitoringObjectList implements Iterator, Countable, ArrayAccess
{
private $dataSet = array();
private $position = 0;
private $dataView = null;
public function __construct(array &$dataset, AccessorStrategy $dataView = null)
{
$this->dataSet = $dataset;
$this->position = 0;
$this->dataView = $dataView;
}
public function count()
{
return count($this->dataSet);
}
public function setPosition($pos)
{
$this->position = $pos;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Return the current element
* @link http://php.net/manual/en/iterator.current.php
* @return mixed Can return any type.
*/
public function current()
{
if ($this->dataView) {
return $this;
}
return $this->dataSet[$this->position];
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Move forward to next element
* @link http://php.net/manual/en/iterator.next.php
* @return void Any returned value is ignored.
*/
public function next()
{
$this->position++;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Return the key of the current element
* @link http://php.net/manual/en/iterator.key.php
* @return mixed scalar on success, or null on failure.
*/
public function key()
{
return $this->position;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Checks if current position is valid
* @link http://php.net/manual/en/iterator.valid.php
* @return boolean The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid()
{
return $this->position < count($this->dataSet);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Rewind the Iterator to the first element
* @link http://php.net/manual/en/iterator.rewind.php
* @return void Any returned value is ignored.
*/
public function rewind()
{
$this->position = 0;
}
public function __isset($name)
{
return $this->dataView->exists($this->dataSet[$this->position], $name);
}
public function __get($name)
{
return $this->dataView->get($this->dataSet[$this->position], $name);
}
public function __set($name, $value)
{
throw new IcingaException('Setting is currently not available for objects');
}
public function offsetExists($offset)
{
return count($this->dataSet) < $offset;
}
public function offsetGet($offset)
{
$res = new MonitoringObjectList($this->dataSet, $this->dataView);
$res->position = $offset;
return $res;
}
public function offsetSet($offset, $value)
{
// non mutable
}
public function offsetUnset($offset)
{
// non mutable
}
}

@ -25,20 +25,16 @@ namespace Icinga\Test {
use RuntimeException;
use Mockery;
use Zend_Config;
use Zend_Controller_Request_Abstract;
use Zend_Controller_Request_HttpTestCase;
use PHPUnit_Framework_TestCase;
use Icinga\Application\Icinga;
use Icinga\Util\DateTimeFactory;
use Icinga\Data\ResourceFactory;
use Icinga\Data\Db\DbConnection;
use Icinga\User\Preferences;
use Icinga\Web\Form;
/**
* Class BaseTestCase
*/
class BaseTestCase extends PHPUnit_Framework_TestCase implements DbTest, FormTest
class BaseTestCase extends PHPUnit_Framework_TestCase implements DbTest
{
/**
* Path to application/
@ -82,13 +78,6 @@ namespace Icinga\Test {
*/
public static $moduleDir;
/**
* Store request for form tests
*
* @var Zend_Controller_Request_HttpTestCase
*/
private $request;
/**
* Resource configuration for different database types
*
@ -151,28 +140,54 @@ namespace Icinga\Test {
public function setUp()
{
parent::setUp();
$requestMock = Mockery::mock('Icinga\Web\Request');
$requestMock->shouldReceive('getPathInfo')->andReturn('')
->shouldReceive('getBaseUrl')->andReturn('/')
->shouldReceive('getQuery')->andReturn(array());
$this->setupIcingaMock($requestMock);
$this->setupIcingaMock();
}
/**
* Setup mock object for the application's bootstrap
*
* @param Zend_Controller_Request_Abstract $request The request to be returned by
* Icinga::app()->getFrontController()->getRequest()
* @return Mockery\Mock
*/
protected function setupIcingaMock(Zend_Controller_Request_Abstract $request)
protected function setupIcingaMock()
{
$requestMock = Mockery::mock('Icinga\Web\Request')->shouldDeferMissing();
$requestMock->shouldReceive('getPathInfo')->andReturn('')->byDefault()
->shouldReceive('getBaseUrl')->andReturn('/')->byDefault()
->shouldReceive('getQuery')->andReturn(array())->byDefault()
->shouldReceive('getParam')->with(Mockery::type('string'), Mockery::type('string'))
->andReturnUsing(function ($name, $default) { return $default; })->byDefault();
$responseMock = Mockery::mock('Icinga\Web\Response')->shouldDeferMissing();
// Can't express this as demeter chains. See: https://github.com/padraic/mockery/issues/59
$bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing();
$bootstrapMock->shouldReceive('getFrontController->getRequest')->andReturnUsing(
function () use ($request) { return $request; }
)->shouldReceive('getApplicationDir')->andReturn(self::$appDir);
$bootstrapMock->shouldReceive('getFrontController')->andReturn($bootstrapMock)
->shouldReceive('getApplicationDir')->andReturn(self::$appDir)
->shouldReceive('getRequest')->andReturn($requestMock)
->shouldReceive('getResponse')->andReturn($responseMock);
Icinga::setApp($bootstrapMock, true);
return $bootstrapMock;
}
/**
* Return the currently active request mock object
*
* @return Icinga\Web\Request
*/
public function getRequestMock()
{
return Icinga::app()->getFrontController()->getRequest();
}
/**
* Return the currently active response mock object
*
* @return Icinga\Web\Response
*/
public function getResponseMock()
{
return Icinga::app()->getFrontController()->getResponse();
}
/**
@ -298,58 +313,6 @@ namespace Icinga\Test {
$adapter->exec('DROP TABLE ' . $table . ';');
}
}
/**
* Instantiate a form
*
* If the form has CSRF protection enabled, creates the form's token element and adds the generated token to the
* request data
*
* @param string $formClass Qualified class name of the form to create. Note that the class has to be
* defined as no attempt is made to require the class before instantiating.
* @param array $requestData Request data for the form
*
* @return Form
* @throws RuntimeException
*/
public function createForm($formClass, array $requestData = array())
{
$form = new $formClass;
// If the form has CSRF protection enabled, add the token to the request data, else all calls to
// isSubmittedAndValid will fail
$form->setSessionId('1234');
$form->initCsrfToken();
$token = $form->getValue($form->getTokenElementName());
if ($token !== null) {
$requestData[$form->getTokenElementName()] = $token;
}
$request = $this->getRequest();
$request->setMethod('POST');
$request->setPost($requestData);
$form->setRequest($request);
$form->setUserPreferences(
new Preferences(
array()
)
);
return $form;
}
/**
* Retrieve test case request object
*
* This is a mock methods borrowed from Zend Controller Test Case to handle form tests properly (#6106)
*
* @return Zend_Controller_Request_HttpTestCase
*/
public function getRequest()
{
if (null === $this->request) {
require_once 'Zend/Controller/Request/HttpTestCase.php';
$this->request = new Zend_Controller_Request_HttpTestCase;
}
return $this->request;
}
}
BaseTestCase::setupTimezone();

@ -1,23 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Test;
use Icinga\Web\Form;
/**
* Interface to test form objects
*/
interface FormTest
{
/**
* Instantiate a new form object
*
* @param string $formClass Form class to instantiate
* @param array $requestData Request data for the form
*
* @return Form
*/
public function createForm($formClass, array $requestData = array());
}

@ -7,7 +7,6 @@ namespace Icinga;
use DateTimeZone;
use InvalidArgumentException;
use Icinga\User\Preferences;
use Icinga\User\Message;
/**
* This class represents an authorized user
@ -98,13 +97,6 @@ class User
*/
protected $preferences;
/**
* Queued notifications for this user.
*
* @var array()
*/
protected $messages;
/**
* Creates a user object given the provided information
*
@ -187,9 +179,9 @@ class User
}
/**
* Return permission information for this user
* Get the user's permissions
*
* @return array
* @return array
*/
public function getPermissions()
{
@ -197,13 +189,19 @@ class User
}
/**
* Setter for permissions
* Set the user's permissions
*
* @param array $permissions
* @param array $permissions
*
* @return $this
*/
public function setPermissions(array $permissions)
{
$this->permissions = $permissions;
natcasesort($permissions);
if (! empty($permissions)) {
$this->permissions = array_combine($permissions, $permissions);
}
return $this;
}
/**
@ -382,38 +380,6 @@ class User
return new DateTimeZone($tz);
}
/**
* Add a message that can be accessed from future requests, to this user.
*
* This function does NOT automatically write to the session, messages will not be persisted until you do.
*
* @param Message $msg The message
*/
public function addMessage(Message $msg)
{
$this->messages[] = $msg;
}
/**
* Get all currently pending messages
*
* @return array The messages
*/
public function getMessages()
{
return isset($this->messages) ? $this->messages : array();
}
/**
* Remove all messages from this user
*
* This function does NOT automatically write the session, messages will not be persisted until you do.
*/
public function clearMessages()
{
$this->messages = null;
}
/**
* Set additional remote user information
*
@ -442,6 +408,33 @@ class User
*/
public function isRemoteUser()
{
return (count($this->remoteUserInformation)) ? true : false;
return ! empty($this->remoteUserInformation);
}
/**
* Whether the user has a given permission
*
* @param string $permission
*
* @return bool
*/
public function can($permission)
{
if (isset($this->permissions['*']) || isset($this->permissions[$permission])) {
return true;
}
foreach ($this->permissions as $permitted) {
$wildcard = strpos($permitted, '*');
if ($wildcard !== false) {
if (substr($permission, 0, $wildcard) === substr($permitted, 0, $wildcard)) {
return true;
} else {
if ($permission === $permitted) {
return true;
}
}
}
}
return false;
}
}

@ -1,59 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\User;
use Zend_Log;
/**
* Class Message
*
* A Message with an additional logging level to indicate the type.
*
* @package Icinga\User
*/
class Message
{
/**
* The content of this message
*
* @var string
*/
private $message;
/**
* The logging-level of this message
*/
private $level;
/**
* Create a new Message
*
* @param string $message The message content
* @param $level The status of the message
* * Zend_Log::INFO
* * Zend_Log::ERR
*/
public function __construct($message, $level = Zend_Log::INFO)
{
$this->message = $message;
$this->level = $level;
}
/**
* @return string
*/
public function getMessage()
{
return $this->message;
}
/**
* @return The
*/
public function getLevel()
{
return $this->level;
}
}

@ -79,19 +79,38 @@ class Preferences implements Countable
}
/**
* Retrieve a preference and return $default if the preference is not set
* Retrieve a preference section
*
* @param string $name
* @param mixed $default
*
* @return mixed
* @return array|null
*/
public function get($name, $default = null)
public function get($name)
{
if (array_key_exists($name, $this->preferences)) {
return $this->preferences[$name];
}
return null;
}
/**
* Retrieve a value from a specific section
*
* @param string $section
* @param string $name
* @param null $default
*
* @return array|null
*/
public function getValue($section, $name, $default = null)
{
if (array_key_exists($section, $this->preferences)
&& array_key_exists($name, $this->preferences[$section])
) {
return $this->preferences[$section][$name];
}
return $default;
}

@ -76,4 +76,18 @@ class DateTimeFactory implements ConfigAwareFactory
{
return new DateTime($time, $timeZone !== null ? $timeZone : self::$timeZone);
}
/**
* Check whether a variable is a Unix timestamp
*
* @param mixed $timestamp
*
* @return bool
*/
public static function isUnixTimestamp($timestamp)
{
return (is_int($timestamp) || ctype_digit($timestamp))
&& ($timestamp <= PHP_INT_MAX)
&& ($timestamp >= ~PHP_INT_MAX);
}
}

Some files were not shown because too many files have changed in this diff Show More