commit
4fa2340552
|
@ -601,27 +601,15 @@ exec { 'create-pgsql-icingaweb-db':
|
|||
require => Service['postgresql']
|
||||
}
|
||||
|
||||
exec { 'populate-icingaweb-mysql-db-accounts':
|
||||
exec { 'populate-icingaweb-mysql-db-tables':
|
||||
unless => 'mysql -uicingaweb -picingaweb icingaweb -e "SELECT * FROM account;" &> /dev/null',
|
||||
command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/accounts.mysql.sql',
|
||||
command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/mysql.sql',
|
||||
require => [ Exec['create-mysql-icingaweb-db'] ]
|
||||
}
|
||||
|
||||
exec { 'populate-icingweba-pgsql-db-accounts':
|
||||
exec { 'populate-icingweba-pgsql-db-tables':
|
||||
unless => 'psql -U icingaweb -d icingaweb -c "SELECT * FROM account;" &> /dev/null',
|
||||
command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/accounts.pgsql.sql',
|
||||
require => [ Exec['create-pgsql-icingaweb-db'] ]
|
||||
}
|
||||
|
||||
exec { 'populate-icingaweb-mysql-db-preferences':
|
||||
unless => 'mysql -uicingaweb -picingaweb icingaweb -e "SELECT * FROM preference;" &> /dev/null',
|
||||
command => 'mysql -uicingaweb -picingaweb icingaweb < /vagrant/etc/schema/preferences.mysql.sql',
|
||||
require => [ Exec['create-mysql-icingaweb-db'] ]
|
||||
}
|
||||
|
||||
exec { 'populate-icingweba-pgsql-db-preferences':
|
||||
unless => 'psql -U icingaweb -d icingaweb -c "SELECT * FROM preference;" &> /dev/null',
|
||||
command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/preferences.pgsql.sql',
|
||||
command => 'sudo -u postgres psql -U icingaweb -d icingaweb -f /vagrant/etc/schema/pgsql.sql',
|
||||
require => [ Exec['create-pgsql-icingaweb-db'] ]
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,17 @@ class AuthenticationController extends ActionController
|
|||
*/
|
||||
public function loginAction()
|
||||
{
|
||||
if (@file_exists(Config::$configDir . '/setup.token')) {
|
||||
try {
|
||||
$config = Config::app()->toArray();
|
||||
if (empty($config)) {
|
||||
$this->redirectNow(Url::fromPath('setup'));
|
||||
}
|
||||
} catch (NotReadableError $e) {
|
||||
// Gets thrown in case of insufficient permission only
|
||||
}
|
||||
}
|
||||
|
||||
$auth = $this->Auth();
|
||||
$this->view->form = $form = new LoginForm();
|
||||
$this->view->title = $this->translate('Icingaweb Login');
|
||||
|
@ -93,30 +104,30 @@ class AuthenticationController extends ActionController
|
|||
}
|
||||
}
|
||||
if ($backendsTried === 0) {
|
||||
throw new ConfigurationError(
|
||||
$this->view->form->addError(
|
||||
$this->translate(
|
||||
'No authentication methods available. Did you create'
|
||||
. ' authentication.ini when installing Icinga Web 2?'
|
||||
. ' authentication.ini when setting up Icinga Web 2?'
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($backendsTried === $backendsWithError) {
|
||||
throw new ConfigurationError(
|
||||
} else if ($backendsTried === $backendsWithError) {
|
||||
$this->view->form->addError(
|
||||
$this->translate(
|
||||
'All configured authentication methods failed.'
|
||||
. ' Please check the system log or Icinga Web 2 log for more information.'
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($backendsWithError) {
|
||||
$this->view->form->getElement('username')->addError(
|
||||
} elseif ($backendsWithError) {
|
||||
$this->view->form->addError(
|
||||
$this->translate(
|
||||
'Please note that not all authentication methods were available.'
|
||||
. ' Check the system log or Icinga Web 2 log for more information.'
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->view->form->getElement('password')->addError($this->translate('Incorrect username or password'));
|
||||
if ($backendsTried > 0 && $backendsTried !== $backendsWithError) {
|
||||
$this->view->form->getElement('password')->addError($this->translate('Incorrect username or password'));
|
||||
}
|
||||
} elseif ($request->isGet()) {
|
||||
$user = new User('');
|
||||
foreach ($chain as $backend) {
|
||||
|
@ -134,6 +145,8 @@ class AuthenticationController extends ActionController
|
|||
} catch (Exception $e) {
|
||||
$this->view->errorInfo = $e->getMessage();
|
||||
}
|
||||
|
||||
$this->view->configMissing = is_dir(Config::$configDir) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -99,7 +99,7 @@ class ConfigController extends ActionController
|
|||
Notification::success(sprintf($this->translate('Module "%s" enabled'), $module));
|
||||
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
|
||||
} catch (Exception $e) {
|
||||
$this->view->exceptionMesssage = $e->getMessage();
|
||||
$this->view->exceptionMessage = $e->getMessage();
|
||||
$this->view->moduleName = $module;
|
||||
$this->view->action = 'enable';
|
||||
$this->render('module-configuration-error');
|
||||
|
|
|
@ -31,7 +31,9 @@ class AutologinBackendForm extends Form
|
|||
array(
|
||||
'required' => true,
|
||||
'label' => t('Backend Name'),
|
||||
'description' => t('The name of this authentication backend'),
|
||||
'description' => t(
|
||||
'The name of this authentication provider that is used to differentiate it from others'
|
||||
),
|
||||
'validators' => array(
|
||||
array(
|
||||
'Regex',
|
||||
|
@ -50,9 +52,8 @@ class AutologinBackendForm extends Form
|
|||
'text',
|
||||
'strip_username_regexp',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Backend Domain Pattern'),
|
||||
'description' => t('The domain pattern of this authentication backend'),
|
||||
'label' => t('Filter Pattern'),
|
||||
'description' => t('The regular expression to use to strip specific parts off from usernames. Leave empty if you do not want to strip off anything'),
|
||||
'value' => '/\@[^$]+$/',
|
||||
'validators' => array(
|
||||
new Zend_Validate_Callback(function ($value) {
|
||||
|
@ -82,7 +83,7 @@ class AutologinBackendForm extends Form
|
|||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidAuthenticationBackend(Form $form)
|
||||
public static function isValidAuthenticationBackend(Form $form)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
|
@ -54,7 +55,9 @@ class DbBackendForm extends Form
|
|||
array(
|
||||
'required' => true,
|
||||
'label' => t('Backend Name'),
|
||||
'description' => t('The name of this authentication provider'),
|
||||
'description' => t(
|
||||
'The name of this authentication provider that is used to differentiate it from others'
|
||||
),
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
@ -88,7 +91,7 @@ class DbBackendForm extends Form
|
|||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === $this->isValidAuthenticationBackend($this)) {
|
||||
if (false === static::isValidAuthenticationBackend($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -100,21 +103,29 @@ class DbBackendForm extends Form
|
|||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidAuthenticationBackend(Form $form)
|
||||
public static function isValidAuthenticationBackend(Form $form)
|
||||
{
|
||||
$element = $form->getElement('resource');
|
||||
|
||||
try {
|
||||
$dbUserBackend = new DbUserBackend(ResourceFactory::create($element->getValue()));
|
||||
$dbUserBackend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig()));
|
||||
if ($dbUserBackend->count() < 1) {
|
||||
$element->addError(t('No users found under the specified database backend'));
|
||||
$form->addError(t('No users found under the specified database backend'));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$element->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
||||
$form->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the chosen resource
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getResourceConfig()
|
||||
{
|
||||
return ResourceFactory::getResourceConfig($this->getValue('resource'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Icinga\Authentication\Backend\LdapUserBackend;
|
||||
|
||||
/**
|
||||
|
@ -54,7 +56,9 @@ class LdapBackendForm extends Form
|
|||
array(
|
||||
'required' => true,
|
||||
'label' => t('Backend Name'),
|
||||
'description' => t('The name of this authentication backend')
|
||||
'description' => t(
|
||||
'The name of this authentication provider that is used to differentiate it from others'
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
@ -97,7 +101,16 @@ class LdapBackendForm extends Form
|
|||
'value' => 'ldap'
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'base_dn',
|
||||
array(
|
||||
'required' => false,
|
||||
'label' => t('Base DN'),
|
||||
'description' => t('The path where users can be found on the ldap server. ' .
|
||||
' Leave empty to select all users available on the specified resource.')
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -108,7 +121,7 @@ class LdapBackendForm extends Form
|
|||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === $this->isValidAuthenticationBackend($this)) {
|
||||
if (false === static::isValidAuthenticationBackend($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -120,22 +133,34 @@ class LdapBackendForm extends Form
|
|||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidAuthenticationBackend(Form $form)
|
||||
public static function isValidAuthenticationBackend(Form $form)
|
||||
{
|
||||
$element = $form->getElement('resource');
|
||||
|
||||
try {
|
||||
$ldapUserBackend = new LdapUserBackend(
|
||||
ResourceFactory::create($element->getValue()),
|
||||
ResourceFactory::createResource($form->getResourceConfig()),
|
||||
$form->getElement('user_class')->getValue(),
|
||||
$form->getElement('user_name_attribute')->getValue()
|
||||
$form->getElement('user_name_attribute')->getValue(),
|
||||
$form->getElement('base_dn')->getValue()
|
||||
);
|
||||
$ldapUserBackend->assertAuthenticationPossible();
|
||||
} catch (AuthenticationException $e) {
|
||||
$form->addError($e->getMessage());
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$element->addError(sprintf(t('Connection validation failed: %s'), $e->getMessage()));
|
||||
$form->addError(sprintf(t('Unable to validate authentication: %s'), $e->getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the chosen resource
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getResourceConfig()
|
||||
{
|
||||
return ResourceFactory::getResourceConfig($this->getValue('resource'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use Icinga\Form\ConfigForm;
|
|||
use Icinga\Web\Notification;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Form\Config\Authentication\DbBackendForm;
|
||||
use Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||
|
@ -39,8 +40,6 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
* @param Config $resources The resource configuration
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws ConfigurationError In case no resources are available for authentication
|
||||
*/
|
||||
public function setResourceConfig(Config $resourceConfig)
|
||||
{
|
||||
|
@ -49,10 +48,6 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
$resources[strtolower($resource->type)][] = $name;
|
||||
}
|
||||
|
||||
if (empty($resources)) {
|
||||
throw new ConfigurationError(t('Could not find any resources for authentication'));
|
||||
}
|
||||
|
||||
$this->resources = $resources;
|
||||
return $this;
|
||||
}
|
||||
|
@ -201,7 +196,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
{
|
||||
if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) {
|
||||
$backendForm = $this->getBackendForm($this->getElement('type')->getValue());
|
||||
if (false === $backendForm->isValidAuthenticationBackend($this)) {
|
||||
if (false === $backendForm::isValidAuthenticationBackend($this)) {
|
||||
$this->addElement($this->getForceCreationCheckbox());
|
||||
return false;
|
||||
}
|
||||
|
@ -251,6 +246,17 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
$configValues['type'] = $configValues['backend'];
|
||||
$configValues['name'] = $authBackend;
|
||||
$this->populate($configValues);
|
||||
} elseif (empty($this->resources)) {
|
||||
$autologinBackends = array_filter(
|
||||
$this->config->toArray(),
|
||||
function ($authBackendCfg) {
|
||||
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
|
||||
}
|
||||
);
|
||||
|
||||
if (false === empty($autologinBackends)) {
|
||||
throw new ConfigurationError(t('Could not find any resources for authentication'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +286,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
public function createElements(array $formData)
|
||||
{
|
||||
$backendTypes = array();
|
||||
$backendType = isset($formData['type']) ? $formData['type'] : 'db';
|
||||
$backendType = isset($formData['type']) ? $formData['type'] : null;
|
||||
|
||||
if (isset($this->resources['db'])) {
|
||||
$backendTypes['db'] = t('Database');
|
||||
|
@ -299,6 +305,10 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
$backendTypes['autologin'] = t('Autologin');
|
||||
}
|
||||
|
||||
if ($backendType === null) {
|
||||
$backendType = key($backendTypes);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'type',
|
||||
|
@ -307,7 +317,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
'required' => true,
|
||||
'autosubmit' => true,
|
||||
'label' => t('Backend Type'),
|
||||
'description' => t('The type of the resource to use for this authenticaton backend'),
|
||||
'description' => t('The type of the resource to use for this authenticaton provider'),
|
||||
'multiOptions' => $backendTypes
|
||||
)
|
||||
);
|
||||
|
@ -319,4 +329,14 @@ class AuthenticationBackendConfigForm extends ConfigForm
|
|||
|
||||
$this->addElements($this->getBackendForm($backendType)->createElements($formData)->getElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the chosen resource
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getResourceConfig()
|
||||
{
|
||||
return ResourceFactory::getResourceConfig($this->getValue('resource'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,13 @@ class LoggingConfigForm extends Form
|
|||
'logging_log',
|
||||
array(
|
||||
'required' => true,
|
||||
'class' => 'autosubmit',
|
||||
'autosubmit' => true,
|
||||
'label' => t('Logging Type'),
|
||||
'description' => t('The type of logging to utilize.'),
|
||||
'multiOptions' => array(
|
||||
'syslog' => 'Syslog',
|
||||
'file' => t('File'),
|
||||
'none' => t('None')
|
||||
'file' => t('File', 'app.config.logging.type'),
|
||||
'none' => t('None', 'app.config.logging.type')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -50,16 +50,16 @@ class LoggingConfigForm extends Form
|
|||
'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')
|
||||
Logger::$levels[Logger::ERROR] => t('Error', 'app.config.logging.level'),
|
||||
Logger::$levels[Logger::WARNING] => t('Warning', 'app.config.logging.level'),
|
||||
Logger::$levels[Logger::INFO] => t('Information', 'app.config.logging.level'),
|
||||
Logger::$levels[Logger::DEBUG] => t('Debug', 'app.config.logging.level')
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($formData['logging_log']) && $formData['logging_log'] === 'syslog') {
|
||||
if (false === isset($formData['logging_log']) || $formData['logging_log'] === 'syslog') {
|
||||
$this->addElement(
|
||||
'text',
|
||||
'logging_application',
|
||||
|
|
|
@ -10,6 +10,7 @@ use Icinga\Web\Form;
|
|||
use Icinga\Web\Request;
|
||||
use Icinga\Web\Form\Element\Number;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Application\Platform;
|
||||
|
||||
/**
|
||||
* Form class for adding/modifying database resources
|
||||
|
@ -29,6 +30,23 @@ class DbResourceForm extends Form
|
|||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$dbChoices = array();
|
||||
if (Platform::zendClassExists('Zend_Db_Adapter_Pdo_Mysql')) {
|
||||
$dbChoices['mysql'] = 'MySQL';
|
||||
}
|
||||
if (Platform::zendClassExists('Zend_Db_Adapter_Pdo_Pgsql')) {
|
||||
$dbChoices['pgsql'] = 'PostgreSQL';
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Resource Name'),
|
||||
'description' => t('The unique name of this resource')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'select',
|
||||
'db',
|
||||
|
@ -36,11 +54,7 @@ class DbResourceForm extends Form
|
|||
'required' => true,
|
||||
'label' => t('Database Type'),
|
||||
'description' => t('The type of SQL database'),
|
||||
'multiOptions' => array(
|
||||
'mysql' => 'MySQL',
|
||||
'pgsql' => 'PostgreSQL'
|
||||
//'oracle' => 'Oracle'
|
||||
)
|
||||
'multiOptions' => $dbChoices
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
@ -103,7 +117,7 @@ class DbResourceForm extends Form
|
|||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === $this->isValidResource($this)) {
|
||||
if (false === static::isValidResource($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +129,7 @@ class DbResourceForm extends Form
|
|||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidResource(Form $form)
|
||||
public static function isValidResource(Form $form)
|
||||
{
|
||||
try {
|
||||
$resource = ResourceFactory::createResource(new Config($form->getValues()));
|
||||
|
|
|
@ -25,6 +25,15 @@ class FileResourceForm extends Form
|
|||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Resource Name'),
|
||||
'description' => t('The unique name of this resource')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'filename',
|
||||
|
|
|
@ -29,6 +29,15 @@ class LdapResourceForm extends Form
|
|||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Resource Name'),
|
||||
'description' => t('The unique name of this resource')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'hostname',
|
||||
|
@ -56,7 +65,7 @@ class LdapResourceForm extends Form
|
|||
array(
|
||||
'required' => true,
|
||||
'label' => t('Root DN'),
|
||||
'description' => t('The path where users can be found on the ldap server')
|
||||
'description' => t('Only the root and its child nodes will be accessible on this resource.')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
@ -89,7 +98,7 @@ class LdapResourceForm extends Form
|
|||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === $this->isValidResource($this)) {
|
||||
if (false === static::isValidResource($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -101,11 +110,17 @@ class LdapResourceForm extends Form
|
|||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidResource(Form $form)
|
||||
public static function isValidResource(Form $form)
|
||||
{
|
||||
try {
|
||||
$resource = ResourceFactory::createResource(new Config($form->getValues()));
|
||||
$resource->connect();
|
||||
if (false === $resource->testCredentials(
|
||||
$form->getElement('bind_dn')->getValue(),
|
||||
$form->getElement('bind_pw')->getValue()
|
||||
)
|
||||
) {
|
||||
throw new Exception();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
|
||||
return false;
|
||||
|
|
|
@ -29,6 +29,15 @@ class LivestatusResourceForm extends Form
|
|||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Resource Name'),
|
||||
'description' => t('The unique name of this resource')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'socket',
|
||||
|
@ -50,7 +59,7 @@ class LivestatusResourceForm extends Form
|
|||
*/
|
||||
public function onSuccess(Request $request)
|
||||
{
|
||||
if (false === $this->isValidResource($this)) {
|
||||
if (false === static::isValidResource($this)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +71,7 @@ class LivestatusResourceForm extends Form
|
|||
*
|
||||
* @return bool Whether validation succeeded or not
|
||||
*/
|
||||
public function isValidResource(Form $form)
|
||||
public static function isValidResource(Form $form)
|
||||
{
|
||||
try {
|
||||
$resource = ResourceFactory::createResource(new Config($form->getValues()));
|
||||
|
|
|
@ -132,7 +132,7 @@ class ResourceConfigForm extends ConfigForm
|
|||
{
|
||||
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)) {
|
||||
if (method_exists($resourceForm, 'isValidResource') && false === $resourceForm::isValidResource($this)) {
|
||||
$this->addElement($this->getForceCreationCheckbox());
|
||||
return false;
|
||||
}
|
||||
|
@ -220,15 +220,6 @@ class ResourceConfigForm extends ConfigForm
|
|||
$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',
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Form;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Protocol\Ldap\Exception as LdapException;
|
||||
use Icinga\Protocol\Ldap\Connection;
|
||||
use Icinga\Protocol\Dns;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Form class for application-wide and logging specific settings
|
||||
*/
|
||||
class LdapDiscoveryForm extends Form
|
||||
{
|
||||
/**
|
||||
* The discovered server settings
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $capabilities = null;
|
||||
|
||||
/**
|
||||
* The discovered root_dn
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $namingContext = null;
|
||||
|
||||
/**
|
||||
* The working domain name
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
private $domain = null;
|
||||
|
||||
/**
|
||||
* The working port name
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $port = 389;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_ldap_discovery');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'domain',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Search Domain'),
|
||||
'description' => t('Search this domain for records of available servers.'),
|
||||
)
|
||||
);
|
||||
|
||||
if (false) {
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'additional_description',
|
||||
array(
|
||||
'value' => t('No Ldap servers found on this domain.'
|
||||
. ' You can try to specify host and port and try again, or just skip this step and '
|
||||
. 'configure the server manually.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'hostname',
|
||||
array(
|
||||
'required' => false,
|
||||
'label' => t('Host'),
|
||||
'description' => t('IP or host name to search.'),
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'port',
|
||||
array(
|
||||
'required' => false,
|
||||
'label' => t('Port'),
|
||||
'description' => t('Port', 389),
|
||||
)
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->discover($this->getValue('domain'))) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function discover($domain)
|
||||
{
|
||||
// Attempt 1: Connect to the domain directly
|
||||
if ($this->discoverCapabilities(array(
|
||||
'hostname' => $domain,
|
||||
'port' => 389)
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempt 2: Discover all available ldap dns records and connect to the first one
|
||||
$cap = false;
|
||||
$records = array_merge(Dns::getSrvRecords($domain, 'ldap'), Dns::getSrvRecords($domain, 'ldaps'));
|
||||
if (isset($records[0])) {
|
||||
$record = $records[0];
|
||||
if (isset($record['port'])) {
|
||||
$cap = $this->discoverCapabilities(array(
|
||||
'hostname' => $record['target'],
|
||||
'port' => $record['port']
|
||||
));
|
||||
} else {
|
||||
$cap = $this->discoverCapabilities(array(
|
||||
'hostname' => $record['target'],
|
||||
'port' => 389
|
||||
));
|
||||
}
|
||||
}
|
||||
return $cap;
|
||||
}
|
||||
|
||||
private function discoverCapabilities($config)
|
||||
{
|
||||
$conn = new Connection(new Config($config));
|
||||
try {
|
||||
$conn->connect();
|
||||
$this->capabilities = $conn->getCapabilities();
|
||||
$this->namingContext = $conn->getDefaultNamingContext();
|
||||
$this->port = $config['port'];
|
||||
$this->domain = $config['hostname'];
|
||||
return true;
|
||||
} catch (LdapException $e) {
|
||||
Logger::info(
|
||||
'Ldap discovery for ' . $config['hostname'] . ':' . $config['port'] . ' failed: ' . $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function suggestResourceSettings()
|
||||
{
|
||||
if (! isset($this->capabilities)) {
|
||||
return array();
|
||||
}
|
||||
if ($this->capabilities->msCapabilities->ActiveDirectoryOid) {
|
||||
return array(
|
||||
'hostname' => $this->domain,
|
||||
'port' => $this->port,
|
||||
'root_dn' => $this->namingContext
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'hostname' => $this->domain,
|
||||
'port' => $this->port,
|
||||
'root_dn' => $this->namingContext
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasSuggestion()
|
||||
{
|
||||
return isset($this->capabilities);
|
||||
}
|
||||
|
||||
public function suggestBackendSettings()
|
||||
{
|
||||
if (! isset($this->capabilities)) {
|
||||
return array();
|
||||
}
|
||||
if ($this->capabilities->msCapabilities->ActiveDirectoryOid) {
|
||||
return array(
|
||||
'base_dn' => $this->namingContext,
|
||||
'user_class' => 'user',
|
||||
'user_name_attribute' => 'sAMAccountName'
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'base_dn' => $this->namingContext,
|
||||
'user_class' => 'inetOrgPerson',
|
||||
'user_name_attribute' => 'uid'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function isAd()
|
||||
{
|
||||
return $this->capabilities->msCapabilities->ActiveDirectoryOid;
|
||||
}
|
||||
}
|
|
@ -100,10 +100,7 @@ class PreferenceForm extends Form
|
|||
}
|
||||
$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();
|
||||
Session::getSession()->user->setPreferences($this->preferences);
|
||||
|
||||
try {
|
||||
$this->save();
|
||||
|
|
|
@ -15,5 +15,17 @@
|
|||
<?php endif ?>
|
||||
<?= $this->form ?>
|
||||
<div class="footer">Icinga Web 2 © 2013-2014 Icinga Team</div>
|
||||
<?php if ($configMissing): ?>
|
||||
<div class="config-note"><?= sprintf(
|
||||
t(
|
||||
'You seem not to have Icinga Web 2 configured yet so it\'s not possible to log in without any defined '
|
||||
. 'authentication method. Please define a authentication method by following the instructions in the'
|
||||
. ' %1$sdocumentation%3$s or by using our %2$sweb-based setup-wizard%3$s.'
|
||||
),
|
||||
'<a href="http://docs.icinga.org/" title="Icinga Web 2 Documentation">', // TODO: Documentation link
|
||||
'<a href="' . $this->href('setup') . '" title="Icinga Web 2 Setup-Wizard">',
|
||||
'</a>'
|
||||
); ?></div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -74,8 +74,7 @@ create all database tables. You will find the installation guides for the differ
|
|||
>
|
||||
> RPM packages install the schema into /usr/share/doc/icingaweb-<version>/schema
|
||||
|
||||
bash$ mysql -u root -p icingaweb < etc/schema/accounts.mysql.sql
|
||||
bash$ mysql -u root -p icingaweb < etc/schema/preferences.mysql.sql
|
||||
bash$ mysql -u root -p icingaweb < etc/schema/mysql.sql
|
||||
|
||||
|
||||
#### PostgreSQL
|
||||
|
@ -108,8 +107,7 @@ And restart your database ('service postgresql restart' or '/etc/init.d/postgres
|
|||
>
|
||||
> RPM packages install the schema into /usr/share/doc/icingaweb-<version>/schema
|
||||
|
||||
bash$ psql -U icingaweb -a -f etc/schema/accounts.pgsql.sql
|
||||
bash$ psql -U icingaweb -a -f etc/schema/preferences.pgsql.sql
|
||||
bash$ psql -U icingaweb -a -f etc/schema/pgsql.sql
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
create table account (
|
||||
`username` varchar(255) COLLATE latin1_general_ci NOT NULL,
|
||||
`salt` varchar(255) NOT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`active` tinyint(1) DEFAULT NULL,
|
||||
PRIMARY KEY (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
/*
|
||||
* user: icingaadmin
|
||||
* password: icinga
|
||||
*/
|
||||
INSERT INTO account (
|
||||
`username`,
|
||||
`salt`,
|
||||
`password`,
|
||||
`active`
|
||||
)
|
||||
VALUES (
|
||||
'icingaadmin',
|
||||
'57cfd5746224be4f60c25d4e8514bec35ad2d01810723a138756b285898e71b2',
|
||||
'43f8e0588eb39f1a41383b48def0b1fdc45e79b8f67194cccee4453eb3f4ea13',
|
||||
1
|
||||
);
|
|
@ -1,28 +0,0 @@
|
|||
create table "account" (
|
||||
"username" character varying(255) NOT NULL,
|
||||
"salt" character varying(255),
|
||||
"password" character varying(255) NOT NULL,
|
||||
"active" boolean
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY "account"
|
||||
ADD CONSTRAINT account_pkey PRIMARY KEY ("username");
|
||||
|
||||
CREATE UNIQUE INDEX username_lower_unique_idx ON "account" USING btree (lower((username)::text));
|
||||
|
||||
/*
|
||||
* user: icingaadmin
|
||||
* password: icinga
|
||||
*/
|
||||
INSERT INTO "account" (
|
||||
"username",
|
||||
"salt",
|
||||
"password",
|
||||
"active"
|
||||
)
|
||||
VALUES (
|
||||
'icingaadmin',
|
||||
'57cfd5746224be4f60c25d4e8514bec35ad2d01810723a138756b285898e71b2',
|
||||
'43f8e0588eb39f1a41383b48def0b1fdc45e79b8f67194cccee4453eb3f4ea13',
|
||||
true
|
||||
);
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Table "icingaweb_group"
|
||||
*/
|
||||
CREATE TABLE "icingaweb_group" (
|
||||
"name" character varying(64) NOT NULL,
|
||||
"parent" character varying(64) NULL DEFAULT NULL,
|
||||
"ctime" timestamp NULL DEFAULT NULL,
|
||||
"mtime" timestamp NULL DEFAULT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY "icingaweb_group"
|
||||
ADD CONSTRAINT pk_icingaweb_group
|
||||
PRIMARY KEY (
|
||||
"name"
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_icingaweb_group
|
||||
ON "icingaweb_group"
|
||||
USING btree (
|
||||
lower((name)::text)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Table "icingaweb_group_membership"
|
||||
*/
|
||||
CREATE TABLE "icingaweb_group_membership" (
|
||||
"group_name" character varying(64) NOT NULL,
|
||||
"username" character varying(64) NOT NULL,
|
||||
"ctime" timestamp NULL DEFAULT NULL,
|
||||
"mtime" timestamp NULL DEFAULT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY "icingaweb_group_membership"
|
||||
ADD CONSTRAINT pk_icingaweb_group_membership
|
||||
PRIMARY KEY (
|
||||
"group_name",
|
||||
"username"
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_icingaweb_group_membership
|
||||
ON "icingaweb_group_membership"
|
||||
USING btree (
|
||||
lower((group_name)::text),
|
||||
lower((username)::text)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Table "icingaweb_user"
|
||||
*/
|
||||
CREATE TABLE "icingaweb_user" (
|
||||
"name" character varying(64) NOT NULL,
|
||||
"active" smallint NOT NULL,
|
||||
"password_hash" bytea NOT NULL,
|
||||
"ctime" timestamp NULL DEFAULT NULL,
|
||||
"mtime" timestamp NULL DEFAULT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY "icingaweb_user"
|
||||
ADD CONSTRAINT pk_icingaweb_user
|
||||
PRIMARY KEY (
|
||||
"name"
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_icingaweb_user
|
||||
ON "icingaweb_user"
|
||||
USING btree (
|
||||
lower((name)::text)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Table "icingaweb_user_preference"
|
||||
*/
|
||||
CREATE TABLE "icingaweb_user_preference" (
|
||||
"username" character varying(64) NOT NULL,
|
||||
"name" character varying(64) NOT NULL,
|
||||
"value" character varying(255) NOT NULL,
|
||||
"ctime" timestamp NULL DEFAULT NULL,
|
||||
"mtime" timestamp NULL DEFAULT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY "icingaweb_user_preference"
|
||||
ADD CONSTRAINT pk_icingaweb_user_preference
|
||||
PRIMARY KEY (
|
||||
"username",
|
||||
"name"
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_icingaweb_user_preference
|
||||
ON "icingaweb_user_preference"
|
||||
USING btree (
|
||||
lower((username)::text),
|
||||
lower((name)::text)
|
||||
);
|
|
@ -1,6 +0,0 @@
|
|||
create table `preference`(
|
||||
`username` VARCHAR(255) COLLATE latin1_general_ci NOT NULL,
|
||||
`key` VARCHAR(100) COLLATE latin1_general_ci NOT NULL,
|
||||
`value` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`username`, `key`)
|
||||
) ENGINE=InnoDB;
|
|
@ -1,10 +0,0 @@
|
|||
create table "preference"(
|
||||
"username" VARCHAR(255) NOT NULL,
|
||||
"key" VARCHAR(100) NOT NULL,
|
||||
"value" VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY "preference"
|
||||
ADD CONSTRAINT preference_pkey PRIMARY KEY ("username", "key");
|
||||
|
||||
CREATE UNIQUE INDEX username_and_key_lower_unique_idx ON "preference" USING btree (lower((username)::text), lower((key)::text));
|
|
@ -13,6 +13,7 @@ use Icinga\Exception\NotReadableError;
|
|||
use Icinga\Application\Logger;
|
||||
use Icinga\Util\DateTimeFactory;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
/**
|
||||
|
@ -337,6 +338,21 @@ abstract class ApplicationBootstrap
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all core modules
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function loadCoreModules()
|
||||
{
|
||||
try {
|
||||
$this->moduleManager->loadCoreModules();
|
||||
} catch (NotReadableError $e) {
|
||||
Logger::error(new IcingaException('Cannot load core modules. An exception was thrown:', $e));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all enabled modules
|
||||
*
|
||||
|
@ -377,12 +393,14 @@ abstract class ApplicationBootstrap
|
|||
protected function loadConfig()
|
||||
{
|
||||
Config::$configDir = $this->configDir;
|
||||
|
||||
try {
|
||||
$this->config = Config::app();
|
||||
} catch (NotReadableError $e) {
|
||||
Logger::error(new IcingaException('Cannot load application configuration. An exception was thrown:', $e));
|
||||
$this->config = new Config();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ class Cli extends ApplicationBootstrap
|
|||
->parseBasicParams()
|
||||
->setupLogger()
|
||||
->setupResourceFactory()
|
||||
->setupModuleManager();
|
||||
->setupModuleManager()
|
||||
->loadCoreModules();
|
||||
}
|
||||
|
||||
protected function setupLogging()
|
||||
|
|
|
@ -31,6 +31,7 @@ class EmbeddedWeb extends ApplicationBootstrap
|
|||
->setupErrorHandling()
|
||||
->setupTimezone()
|
||||
->setupModuleManager()
|
||||
->loadCoreModules()
|
||||
->loadEnabledModules();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,18 @@ class Manager
|
|||
*/
|
||||
private $modulePaths = array();
|
||||
|
||||
/**
|
||||
* The core modules
|
||||
*
|
||||
* Core modules do not need to be enabled to load and cannot be disabled
|
||||
* by the user. This must not be writable programmatically!
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $coreModules = array(
|
||||
'setup'
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new instance of the module manager
|
||||
*
|
||||
|
@ -157,7 +169,21 @@ class Manager
|
|||
}
|
||||
|
||||
/**
|
||||
* Try to set all enabled modules in loaded sate
|
||||
* Try to set all core modules in loaded state
|
||||
*
|
||||
* @return self
|
||||
* @see Manager::loadModule()
|
||||
*/
|
||||
public function loadCoreModules()
|
||||
{
|
||||
foreach ($this->coreModules as $name) {
|
||||
$this->loadModule($name);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to set all enabled modules in loaded state
|
||||
*
|
||||
* @return self
|
||||
* @see Manager::loadModule()
|
||||
|
@ -211,6 +237,8 @@ class Manager
|
|||
'Cannot enable module "%s". Module is not installed.',
|
||||
$name
|
||||
);
|
||||
} elseif (in_array($name, $this->coreModules)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
clearstatcache(true);
|
||||
|
@ -427,7 +455,7 @@ class Manager
|
|||
}
|
||||
|
||||
$installed = $this->listInstalledModules();
|
||||
foreach ($installed as $name) {
|
||||
foreach (array_diff($installed, $this->coreModules) as $name) {
|
||||
$info[$name] = (object) array(
|
||||
'name' => $name,
|
||||
'path' => $this->installedBaseDirs[$name],
|
||||
|
@ -487,11 +515,14 @@ class Manager
|
|||
/**
|
||||
* Detect installed modules from every path provided in modulePaths
|
||||
*
|
||||
* @param array $availableDirs Installed modules location
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function detectInstalledModules()
|
||||
public function detectInstalledModules(array $availableDirs = null)
|
||||
{
|
||||
foreach ($this->modulePaths as $basedir) {
|
||||
$modulePaths = $availableDirs !== null ? $availableDirs : $this->modulePaths;
|
||||
foreach ($modulePaths as $basedir) {
|
||||
$canonical = realpath($basedir);
|
||||
if ($canonical === false) {
|
||||
Logger::warning('Module path "%s" does not exist', $basedir);
|
||||
|
|
|
@ -17,6 +17,7 @@ use Icinga\Web\Hook;
|
|||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\Widget;
|
||||
use Icinga\Web\Widget\Dashboard\Pane;
|
||||
use Icinga\Module\Setup\SetupWizard;
|
||||
use Icinga\Util\File;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
@ -133,6 +134,13 @@ class Module
|
|||
*/
|
||||
private $configTabs = array();
|
||||
|
||||
/**
|
||||
* Provided setup wizard
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $setupWizard;
|
||||
|
||||
/**
|
||||
* Icinga application
|
||||
*
|
||||
|
@ -641,6 +649,31 @@ class Module
|
|||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this module provides a setup wizard
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function providesSetupWizard()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
if (class_exists($this->setupWizard)) {
|
||||
$wizard = new $this->setupWizard;
|
||||
return $wizard instanceof SetupWizard;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this module's setup wizard
|
||||
*
|
||||
* @return SetupWizard
|
||||
*/
|
||||
public function getSetupWizard()
|
||||
{
|
||||
return new $this->setupWizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a named permission
|
||||
|
@ -704,6 +737,19 @@ class Module
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a setup wizard
|
||||
*
|
||||
* @param string $className The name of the class
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function provideSetupWizard($className)
|
||||
{
|
||||
$this->setupWizard = $className;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new namespaces on the autoloader
|
||||
*
|
||||
|
|
|
@ -30,6 +30,16 @@ class Platform
|
|||
*/
|
||||
protected static $fqdn;
|
||||
|
||||
/**
|
||||
* Return the operating system's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getOperatingSystemName()
|
||||
{
|
||||
return php_uname('s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of windows
|
||||
*
|
||||
|
@ -37,7 +47,7 @@ class Platform
|
|||
*/
|
||||
public static function isWindows()
|
||||
{
|
||||
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
|
||||
return strtoupper(substr(self::getOperatingSystemName(), 0, 3)) === 'WIN';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,7 +57,7 @@ class Platform
|
|||
*/
|
||||
public static function isLinux()
|
||||
{
|
||||
return strtoupper(substr(PHP_OS, 0, 5)) === 'LINUX';
|
||||
return strtoupper(substr(self::getOperatingSystemName(), 0, 5)) === 'LINUX';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,7 +126,35 @@ class Platform
|
|||
if (substr(self::$fqdn, 0, strlen(self::$hostname)) === self::$hostname) {
|
||||
self::$domain = substr(self::$fqdn, strlen(self::$hostname) + 1);
|
||||
} else {
|
||||
self::$domain = array_shift(preg_split('~\.~', self::$hostname, 2));
|
||||
$parts = preg_split('~\.~', self::$hostname, 2);
|
||||
self::$domain = array_shift($parts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version of PHP
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPhpVersion()
|
||||
{
|
||||
return phpversion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the username PHP is running as
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPhpUser()
|
||||
{
|
||||
if (static::isWindows()) {
|
||||
return get_current_user(); // http://php.net/manual/en/function.get-current-user.php#75059
|
||||
}
|
||||
|
||||
if (function_exists('posix_geteuid')) {
|
||||
$userInfo = posix_getpwuid(posix_geteuid());
|
||||
return $userInfo['name'];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,4 +169,32 @@ class Platform
|
|||
{
|
||||
return extension_loaded($extensionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value for the given PHP configuration option
|
||||
*
|
||||
* @param string $option The option name for which to return the value
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public static function getPhpConfig($option)
|
||||
{
|
||||
return ini_get($option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given Zend framework class exists
|
||||
*
|
||||
* @param string $name The name of the class to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function zendClassExists($name)
|
||||
{
|
||||
if (class_exists($name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (@include str_replace('_', '/', $name) . '.php') !== false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ class Web extends ApplicationBootstrap
|
|||
->setupZendMvc()
|
||||
->setupFormNamespace()
|
||||
->setupModuleManager()
|
||||
->loadCoreModules()
|
||||
->loadEnabledModules()
|
||||
->setupRoute()
|
||||
->setupPagination();
|
||||
|
|
|
@ -54,7 +54,7 @@ class AutoLoginBackend extends UserBackend
|
|||
if (isset($_SERVER['REMOTE_USER'])) {
|
||||
$username = $_SERVER['REMOTE_USER'];
|
||||
$user->setRemoteUserInformation($username, 'REMOTE_USER');
|
||||
if ($this->stripUsernameRegexp !== null) {
|
||||
if ($this->stripUsernameRegexp) {
|
||||
$stripped = preg_replace($this->stripUsernameRegexp, '', $username);
|
||||
if ($stripped !== false) {
|
||||
// TODO(el): PHP issues a warning when PHP cannot compile the regular expression. Should we log an
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace Icinga\Authentication\Backend;
|
||||
|
||||
use PDO;
|
||||
use Icinga\Authentication\UserBackend;
|
||||
use Icinga\Data\Db\DbConnection;
|
||||
use Icinga\User;
|
||||
|
@ -11,16 +12,29 @@ use Icinga\Exception\AuthenticationException;
|
|||
use Exception;
|
||||
use Zend_Db_Expr;
|
||||
use Zend_Db_Select;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
class DbUserBackend extends UserBackend
|
||||
{
|
||||
/**
|
||||
* The algorithm to use when hashing passwords
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HASH_ALGORITHM = '$1$'; // MD5
|
||||
|
||||
/**
|
||||
* The length of the salt to use when hashing a password
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const SALT_LENGTH = 12; // 12 is required by MD5
|
||||
|
||||
/**
|
||||
* Connection to the database
|
||||
*
|
||||
* @var DbConnection
|
||||
*/
|
||||
private $conn;
|
||||
protected $conn;
|
||||
|
||||
public function __construct(DbConnection $conn)
|
||||
{
|
||||
|
@ -36,45 +50,69 @@ class DbUserBackend extends UserBackend
|
|||
*/
|
||||
public function hasUser(User $user)
|
||||
{
|
||||
$select = new Zend_Db_Select($this->conn->getConnection());
|
||||
$row = $select->from('account', array(new Zend_Db_Expr(1)))
|
||||
->where('username = ?', $user->getUsername())
|
||||
$select = new Zend_Db_Select($this->conn->getDbAdapter());
|
||||
$row = $select->from('icingaweb_user', array(new Zend_Db_Expr(1)))
|
||||
->where('name = ?', $user->getUsername())
|
||||
->query()->fetchObject();
|
||||
|
||||
return ($row !== false) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the given user and return true on success, false on failure and null on error
|
||||
* Add a new user
|
||||
*
|
||||
* @param string $username The name of the new user
|
||||
* @param string $password The new user's password
|
||||
* @param bool $active Whether the user is active
|
||||
*/
|
||||
public function addUser($username, $password, $active = true)
|
||||
{
|
||||
$passwordHash = $this->hashPassword($password);
|
||||
|
||||
$stmt = $this->conn->getDbAdapter()->prepare(
|
||||
'INSERT INTO icingaweb_user VALUES (:name, :active, :password_hash, now(), DEFAULT);'
|
||||
);
|
||||
$stmt->bindParam(':name', $username, PDO::PARAM_STR);
|
||||
$stmt->bindParam(':active', $active, PDO::PARAM_INT);
|
||||
$stmt->bindParam(':password_hash', $passwordHash, PDO::PARAM_LOB);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the hashed password for the given user
|
||||
*
|
||||
* @param string $username The name of the user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPasswordHash($username)
|
||||
{
|
||||
$stmt = $this->conn->getDbAdapter()->prepare(
|
||||
'SELECT password_hash FROM icingaweb_user WHERE name = :name AND active = 1'
|
||||
);
|
||||
$stmt->execute(array(':name' => $username));
|
||||
$stmt->bindColumn(1, $lob, PDO::PARAM_LOB);
|
||||
$stmt->fetch(PDO::FETCH_BOUND);
|
||||
return is_resource($lob) ? stream_get_contents($lob) : $lob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the given user and return true on success, false on failure and throw an exception on error
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool|null
|
||||
* @return bool
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function authenticate(User $user, $password)
|
||||
{
|
||||
try {
|
||||
$salt = $this->getSalt($user->getUsername());
|
||||
if ($salt === null) {
|
||||
return false;
|
||||
}
|
||||
if ($salt === '') {
|
||||
throw new IcingaException(
|
||||
'Cannot find salt for user %s',
|
||||
$user->getUsername()
|
||||
);
|
||||
}
|
||||
|
||||
$select = new Zend_Db_Select($this->conn->getConnection());
|
||||
$row = $select->from('account', array(new Zend_Db_Expr(1)))
|
||||
->where('username = ?', $user->getUsername())
|
||||
->where('active = ?', true)
|
||||
->where('password = ?', $this->hashPassword($password, $salt))
|
||||
->query()->fetchObject();
|
||||
|
||||
return ($row !== false) ? true : false;
|
||||
$passwordHash = $this->getPasswordHash($user->getUsername());
|
||||
$passwordSalt = $this->getSalt($passwordHash);
|
||||
$hashToCompare = $this->hashPassword($password, $passwordSalt);
|
||||
return $hashToCompare === $passwordHash;
|
||||
} catch (Exception $e) {
|
||||
throw new AuthenticationException(
|
||||
'Failed to authenticate user "%s" against backend "%s". An exception was thrown:',
|
||||
|
@ -86,29 +124,40 @@ class DbUserBackend extends UserBackend
|
|||
}
|
||||
|
||||
/**
|
||||
* Get salt by username
|
||||
* Extract salt from the given password hash
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $hash The hashed password
|
||||
*
|
||||
* @return string|null
|
||||
* @return string
|
||||
*/
|
||||
private function getSalt($username)
|
||||
protected function getSalt($hash)
|
||||
{
|
||||
$select = new Zend_Db_Select($this->conn->getConnection());
|
||||
$row = $select->from('account', array('salt'))->where('username = ?', $username)->query()->fetchObject();
|
||||
return ($row !== false) ? $row->salt : null;
|
||||
return substr($hash, strlen(self::HASH_ALGORITHM), self::SALT_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random salt
|
||||
*
|
||||
* The returned salt is safe to be used for hashing a user's password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateSalt()
|
||||
{
|
||||
return openssl_random_pseudo_bytes(self::SALT_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $salt
|
||||
* @param string $password
|
||||
* @param string $salt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function hashPassword($password, $salt) {
|
||||
return hash_hmac('sha256', $password, $salt);
|
||||
protected function hashPassword($password, $salt = null)
|
||||
{
|
||||
return crypt($password, self::HASH_ALGORITHM . ($salt !== null ? $salt : $this->generateSalt()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,12 +167,29 @@ class DbUserBackend extends UserBackend
|
|||
*/
|
||||
public function count()
|
||||
{
|
||||
$select = new Zend_Db_Select($this->conn->getConnection());
|
||||
$select = new Zend_Db_Select($this->conn->getDbAdapter());
|
||||
$row = $select->from(
|
||||
'account',
|
||||
'icingaweb_user',
|
||||
array('count' => 'COUNT(*)')
|
||||
)->query()->fetchObject();
|
||||
|
||||
return ($row !== false) ? $row->count : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of all available users
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listUsers()
|
||||
{
|
||||
$query = $this->conn->select()->from('icingaweb_user', array('name'));
|
||||
|
||||
$users = array();
|
||||
foreach ($query->fetchAll() as $row) {
|
||||
$users[] = $row->name;
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
namespace Icinga\Authentication\Backend;
|
||||
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\User;
|
||||
use Icinga\Authentication\UserBackend;
|
||||
use Icinga\Protocol\Ldap\Connection;
|
||||
|
@ -20,20 +19,36 @@ class LdapUserBackend extends UserBackend
|
|||
**/
|
||||
protected $conn;
|
||||
|
||||
protected $baseDn;
|
||||
|
||||
protected $userClass;
|
||||
|
||||
protected $userNameAttribute;
|
||||
|
||||
protected $groupOptions;
|
||||
|
||||
public function __construct(Connection $conn, $userClass, $userNameAttribute, $groupOptions = null)
|
||||
public function __construct(Connection $conn, $userClass, $userNameAttribute, $baseDn, $groupOptions = null)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->baseDn = trim($baseDn) !== '' ? $baseDn : $conn->getDN();
|
||||
$this->userClass = $userClass;
|
||||
$this->userNameAttribute = $userNameAttribute;
|
||||
$this->groupOptions = $groupOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Icinga\Protocol\Ldap\Query
|
||||
*/
|
||||
protected function selectUsers()
|
||||
{
|
||||
return $this->conn->select()->setBase($this->baseDn)->from(
|
||||
$this->userClass,
|
||||
array(
|
||||
$this->userNameAttribute
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create query
|
||||
*
|
||||
|
@ -41,14 +56,9 @@ class LdapUserBackend extends UserBackend
|
|||
*
|
||||
* @return \Icinga\Protocol\Ldap\Query
|
||||
**/
|
||||
protected function createQuery($username)
|
||||
protected function selectUser($username)
|
||||
{
|
||||
return $this->conn->select()
|
||||
->from(
|
||||
$this->userClass,
|
||||
array($this->userNameAttribute)
|
||||
)
|
||||
->where(
|
||||
return $this->selectUsers()->where(
|
||||
$this->userNameAttribute,
|
||||
str_replace('*', '', $username)
|
||||
);
|
||||
|
@ -69,7 +79,7 @@ class LdapUserBackend extends UserBackend
|
|||
public function assertAuthenticationPossible()
|
||||
{
|
||||
try {
|
||||
$q = $this->conn->select()->from($this->userClass);
|
||||
$q = $this->conn->select()->setBase($this->baseDn)->from($this->userClass);
|
||||
$result = $q->fetchRow();
|
||||
} catch (LdapException $e) {
|
||||
throw new AuthenticationException('Connection not possible.', $e);
|
||||
|
@ -79,7 +89,7 @@ class LdapUserBackend extends UserBackend
|
|||
throw new AuthenticationException(
|
||||
'No objects with objectClass="%s" in DN="%s" found.',
|
||||
$this->userClass,
|
||||
$this->conn->getDN()
|
||||
$this->baseDn
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -99,12 +109,12 @@ class LdapUserBackend extends UserBackend
|
|||
*
|
||||
* @param string $dn
|
||||
*
|
||||
* @return array|null
|
||||
* @return array
|
||||
*/
|
||||
public function getGroups($dn)
|
||||
{
|
||||
if (empty($this->groupOptions) || ! isset($this->groupOptions['group_base_dn'])) {
|
||||
return null;
|
||||
return array();
|
||||
}
|
||||
|
||||
$q = $this->conn->select()
|
||||
|
@ -140,7 +150,7 @@ class LdapUserBackend extends UserBackend
|
|||
public function hasUser(User $user)
|
||||
{
|
||||
$username = $user->getUsername();
|
||||
return $this->conn->fetchOne($this->createQuery($username)) === $username;
|
||||
return $this->conn->fetchOne($this->selectUser($username)) === $username;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,7 +183,7 @@ class LdapUserBackend extends UserBackend
|
|||
return false;
|
||||
}
|
||||
try {
|
||||
$userDn = $this->conn->fetchDN($this->createQuery($user->getUsername()));
|
||||
$userDn = $this->conn->fetchDN($this->selectUser($user->getUsername()));
|
||||
$authenticated = $this->conn->testCredentials(
|
||||
$userDn,
|
||||
$password
|
||||
|
@ -203,14 +213,20 @@ class LdapUserBackend extends UserBackend
|
|||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->conn->count($this->selectUsers());
|
||||
}
|
||||
|
||||
return $this->conn->count(
|
||||
$this->conn->select()->from(
|
||||
$this->userClass,
|
||||
array(
|
||||
$this->userNameAttribute
|
||||
)
|
||||
)
|
||||
);
|
||||
/**
|
||||
* Return the names of all available users
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listUsers()
|
||||
{
|
||||
$users = array();
|
||||
foreach ($this->selectUsers()->fetchAll() as $row) {
|
||||
$users[] = $row->{$this->userNameAttribute};
|
||||
}
|
||||
return $users;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,10 +119,7 @@ class Manager
|
|||
*/
|
||||
public function persistCurrentUser()
|
||||
{
|
||||
$session = Session::getSession();
|
||||
$session->set('user', $this->user);
|
||||
$session->write();
|
||||
$session->refreshId();
|
||||
Session::getSession()->set('user', $this->user)->refreshId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -103,6 +103,7 @@ abstract class UserBackend implements Countable
|
|||
$resource,
|
||||
$backendConfig->get('user_class', 'user'),
|
||||
$backendConfig->get('user_name_attribute', 'sAMAccountName'),
|
||||
$backendConfig->get('base_dn', $resource->getDN()),
|
||||
$groupOptions
|
||||
);
|
||||
break;
|
||||
|
@ -129,6 +130,7 @@ abstract class UserBackend implements Countable
|
|||
$resource,
|
||||
$backendConfig->user_class,
|
||||
$backendConfig->user_name_attribute,
|
||||
$backendConfig->get('base_dn', $resource->getDN()),
|
||||
$groupOptions
|
||||
);
|
||||
break;
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Icinga\File\Ini;
|
|||
|
||||
use Zend_Config;
|
||||
use Zend_Config_Ini;
|
||||
use Zend_Config_Exception;
|
||||
use Zend_Config_Writer_FileAbstract;
|
||||
use Icinga\Application\Config;
|
||||
|
||||
|
@ -21,11 +22,18 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
|
|||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* The mode to set on new files
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $fileMode = 0664;
|
||||
|
||||
/**
|
||||
* Create a new INI writer
|
||||
*
|
||||
* @param array $options Supports all options of Zend_Config_Writer and additional
|
||||
* options for the internal IniEditor:
|
||||
* @param array $options Supports all options of Zend_Config_Writer and additional options:
|
||||
* * filemode: The mode to set on new files
|
||||
* * valueIndentation: The indentation level of the values
|
||||
* * commentIndentation: The indentation level of the comments
|
||||
* * sectionSeparators: The amount of newlines between sections
|
||||
|
@ -58,12 +66,36 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
|
|||
}
|
||||
|
||||
$newconfig = $this->_config;
|
||||
$editor = new IniEditor(file_get_contents($this->_filename), $this->options);
|
||||
$editor = new IniEditor(@file_get_contents($this->_filename), $this->options);
|
||||
$this->diffConfigs($oldconfig, $newconfig, $editor);
|
||||
$this->updateSectionOrder($newconfig, $editor);
|
||||
return $editor->getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write configuration to file and set file mode in case it does not exist yet
|
||||
*
|
||||
* @param string $filename
|
||||
* @param Zend_Config $config
|
||||
* @param bool $exclusiveLock
|
||||
*/
|
||||
public function write($filename = null, Zend_Config $config = null, $exclusiveLock = null)
|
||||
{
|
||||
$filePath = $filename !== null ? $filename : $this->_filename;
|
||||
$setMode = false === file_exists($filePath);
|
||||
|
||||
parent::write($filename, $config, $exclusiveLock);
|
||||
|
||||
if ($setMode) {
|
||||
$mode = isset($this->options['filemode']) ? $this->options['filemode'] : static::$fileMode;
|
||||
$old = umask(0); // Make sure that the mode we're going to set doesn't get mangled
|
||||
if (is_int($mode) && false === @chmod($filePath, $mode)) {
|
||||
throw new Zend_Config_Exception(sprintf('Failed to set file mode "%o" on file "%s"', $mode, $filePath));
|
||||
}
|
||||
umask($old);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a property diff and apply the changes to the editor
|
||||
*
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace Icinga\Protocol;
|
|||
*/
|
||||
class Dns
|
||||
{
|
||||
|
||||
/**
|
||||
* Discover all service records on a given domain
|
||||
*
|
||||
|
@ -17,21 +16,12 @@ class Dns
|
|||
* @param string $service The type of the service, like for example 'ldaps' or 'ldap'
|
||||
* @param string $protocol The transport protocol used by the service, defaults to 'tcp'
|
||||
*
|
||||
* @return array|null An array of all service domains
|
||||
* @return array An array of all found service records
|
||||
*/
|
||||
public static function getSrvRecords($domain, $service, $protocol = 'tcp')
|
||||
{
|
||||
$records = dns_get_record('_' . $service . '._' . $protocol . '.' . $domain, DNS_SRV);
|
||||
if ($records === false) {
|
||||
return null;
|
||||
}
|
||||
$targets = array();
|
||||
foreach ($records as $record) {
|
||||
if (array_key_exists('target', $record)) {
|
||||
$targets[] = $record['target'];
|
||||
}
|
||||
}
|
||||
return $targets;
|
||||
return $records === false ? array() : $records;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,8 +18,6 @@ use Icinga\File\Pdf;
|
|||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Web\Session;
|
||||
use Icinga\Web\UrlParams;
|
||||
use Icinga\Session\SessionNamespace;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Zend_Controller_Action;
|
||||
use Zend_Controller_Action_HelperBroker as ActionHelperBroker;
|
||||
use Zend_Controller_Request_Abstract as Request;
|
||||
|
@ -325,14 +323,24 @@ class ActionController extends Zend_Controller_Action
|
|||
$this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now');
|
||||
}
|
||||
|
||||
$this->shutdownSession();
|
||||
|
||||
$this->getResponse()
|
||||
->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl()))
|
||||
->sendHeaders();
|
||||
|
||||
// TODO: Session shutdown?
|
||||
exit;
|
||||
}
|
||||
|
||||
protected function redirectHttp($url)
|
||||
{
|
||||
if (! $url instanceof Url) {
|
||||
$url = Url::fromPath($url);
|
||||
}
|
||||
$this->shutdownSession();
|
||||
$this->_helper->Redirector->gotoUrlAndExit($url->getRelativeUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to a specific url, updating the browsers URL field
|
||||
*
|
||||
|
@ -343,10 +351,7 @@ class ActionController extends Zend_Controller_Action
|
|||
if ($this->isXhr()) {
|
||||
$this->redirectXhr($url);
|
||||
} else {
|
||||
if (! $url instanceof Url) {
|
||||
$url = Url::fromPath($url);
|
||||
}
|
||||
$this->_helper->Redirector->gotoUrlAndExit($url->getRelativeUrl());
|
||||
$this->redirectHttp($url);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,6 +379,7 @@ class ActionController extends Zend_Controller_Action
|
|||
|
||||
if ($req->getParam('format') === 'pdf') {
|
||||
$layout->setLayout('pdf');
|
||||
$this->shutdownSession();
|
||||
$this->sendAsPdf();
|
||||
exit;
|
||||
}
|
||||
|
@ -381,6 +387,8 @@ class ActionController extends Zend_Controller_Action
|
|||
if ($this->isXhr()) {
|
||||
$this->postDispatchXhr();
|
||||
}
|
||||
|
||||
$this->shutdownSession();
|
||||
}
|
||||
|
||||
protected function postDispatchXhr()
|
||||
|
@ -430,6 +438,14 @@ class ActionController extends Zend_Controller_Action
|
|||
$pdf->renderControllerAction($this);
|
||||
}
|
||||
|
||||
protected function shutdownSession()
|
||||
{
|
||||
$session = Session::getSession();
|
||||
if ($session->hasChanged()) {
|
||||
$session->write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the benchmark
|
||||
*
|
||||
|
|
|
@ -100,11 +100,11 @@ class Form extends Zend_Form
|
|||
* @var array
|
||||
*/
|
||||
public static $defaultElementDecorators = array(
|
||||
'ViewHelper',
|
||||
'Errors',
|
||||
array('Description', array('tag' => 'span', 'class' => 'description')),
|
||||
'Label',
|
||||
array('HtmlTag', array('tag' => 'div'))
|
||||
array('ViewHelper', array('separator' => '')),
|
||||
array('Errors', array('separator' => '')),
|
||||
array('Description', array('tag' => 'span', 'class' => 'description', 'separator' => '')),
|
||||
array('Label', array('separator' => '')),
|
||||
array('HtmlTag', array('tag' => 'div', 'class' => 'element'))
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -463,7 +463,15 @@ class Form extends Zend_Form
|
|||
$el = parent::createElement($type, $name, $options);
|
||||
|
||||
if ($el && $el->getAttrib('autosubmit')) {
|
||||
$el->addDecorator(new NoScriptApply()); // Non-JS environments
|
||||
$noScript = new NoScriptApply(); // Non-JS environments
|
||||
$decorators = $el->getDecorators();
|
||||
$pos = array_search('Zend_Form_Decorator_ViewHelper', array_keys($decorators)) + 1;
|
||||
$el->setDecorators(
|
||||
array_slice($decorators, 0, $pos, true)
|
||||
+ array(get_class($noScript) => $noScript)
|
||||
+ array_slice($decorators, $pos, count($decorators) - $pos, true)
|
||||
);
|
||||
|
||||
$class = $el->getAttrib('class');
|
||||
if (is_array($class)) {
|
||||
$class[] = 'autosubmit';
|
||||
|
@ -473,6 +481,7 @@ class Form extends Zend_Form
|
|||
$class .= ' autosubmit';
|
||||
}
|
||||
$el->setAttrib('class', $class); // JS environments
|
||||
|
||||
unset($el->autosubmit);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Web\Form\Decorator;
|
||||
|
||||
use Zend_Form_Element;
|
||||
use Zend_Form_Decorator_Abstract;
|
||||
|
||||
/**
|
||||
* A decorator that will double a single element of a display group
|
||||
*
|
||||
* The options `condition', `double' and `attributes' can be passed to the constructor and are used to affect whether
|
||||
* the doubling should take effect, which element should be doubled and which HTML attributes should be applied to the
|
||||
* doubled element, respectively.
|
||||
*
|
||||
* `condition' must be an element's name that when it's part of the display group causes the condition to be met.
|
||||
* `double' must be an element's name and must be part of the display group.
|
||||
* `attributes' is just an array of key-value pairs.
|
||||
*
|
||||
* You can also pass `placement' to control whether the doubled element is prepended or appended.
|
||||
*/
|
||||
class ElementDoubler extends Zend_Form_Decorator_Abstract
|
||||
{
|
||||
/**
|
||||
* Return the display group's elements with an additional copy of an element being added if the condition is met
|
||||
*
|
||||
* @param string $content The HTML rendered so far
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render($content)
|
||||
{
|
||||
$group = $this->getElement();
|
||||
if ($group->getElement($this->getOption('condition')) !== null) {
|
||||
if ($this->getPlacement() === static::APPEND) {
|
||||
return $content . $this->applyAttributes($group->getElement($this->getOption('double')))->render();
|
||||
} else { // $this->getPlacement() === static::PREPEND
|
||||
return $this->applyAttributes($group->getElement($this->getOption('double')))->render() . $content;
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all element attributes
|
||||
*
|
||||
* @param Zend_Form_Element $element The element to apply the attributes to
|
||||
*
|
||||
* @return Zend_Form_Element
|
||||
*/
|
||||
protected function applyAttributes(Zend_Form_Element $element)
|
||||
{
|
||||
$attributes = $this->getOption('attributes');
|
||||
if ($attributes !== null) {
|
||||
foreach ($attributes as $name => $value) {
|
||||
$element->setAttrib($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
namespace Icinga\Web\Form\Element;
|
||||
|
||||
use Zend_Form_Element;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* A note
|
||||
|
@ -32,7 +31,15 @@ class Note extends Zend_Form_Element
|
|||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setDecorators(Form::$defaultElementDecorators);
|
||||
if (count($this->getDecorators()) === 0) {
|
||||
$this->setDecorators(array(
|
||||
'ViewHelper',
|
||||
array(
|
||||
'HtmlTag',
|
||||
array('tag' => 'p')
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,35 +75,28 @@ class Notification
|
|||
return;
|
||||
}
|
||||
|
||||
$mo = (object) array(
|
||||
$messages = & Session::getSession()->getByRef('messages');
|
||||
$messages[] = (object) array(
|
||||
'type' => $type,
|
||||
'message' => $message,
|
||||
);
|
||||
|
||||
// Get, change, set - just to be on the safe side:
|
||||
$session = Session::getSession();
|
||||
$msgs = $session->messages;
|
||||
$msgs[] = $mo;
|
||||
$session->messages = $msgs;
|
||||
$session->write();
|
||||
}
|
||||
|
||||
public function hasMessages()
|
||||
{
|
||||
$session = Session::getSession();
|
||||
return !empty($session->messages);
|
||||
return false === empty($session->messages);
|
||||
}
|
||||
|
||||
public function getMessages()
|
||||
{
|
||||
$session = Session::getSession();
|
||||
$msgs = $session->messages;
|
||||
if (false === empty($msgs)) {
|
||||
$messages = $session->messages;
|
||||
if (false === empty($messages)) {
|
||||
$session->messages = array();
|
||||
$session->write();
|
||||
}
|
||||
|
||||
return $msgs;
|
||||
return $messages;
|
||||
}
|
||||
|
||||
final private function __construct()
|
||||
|
|
|
@ -21,6 +21,12 @@ class Response extends Zend_Controller_Response_Http
|
|||
} else {
|
||||
$this->setRedirect($url->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
$session = Session::getSession();
|
||||
if ($session->hasChanged()) {
|
||||
$session->write();
|
||||
}
|
||||
|
||||
$this->sendHeaders();
|
||||
exit;
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ class PhpSession extends Session
|
|||
|
||||
foreach ($_SESSION as $key => $value) {
|
||||
if (strpos($key, self::NAMESPACE_PREFIX) === 0) {
|
||||
$namespace = new SessionNamespace($this);
|
||||
$namespace = new SessionNamespace();
|
||||
$namespace->setAll($value);
|
||||
$this->namespaces[substr($key, strlen(self::NAMESPACE_PREFIX))] = $namespace;
|
||||
} else {
|
||||
|
|
|
@ -75,7 +75,7 @@ abstract class Session extends SessionNamespace
|
|||
unset($this->removedNamespaces[array_search($identifier, $this->removedNamespaces)]);
|
||||
}
|
||||
|
||||
$this->namespaces[$identifier] = new SessionNamespace($this);
|
||||
$this->namespaces[$identifier] = new SessionNamespace();
|
||||
}
|
||||
|
||||
return $this->namespaces[$identifier];
|
||||
|
@ -104,13 +104,22 @@ abstract class Session extends SessionNamespace
|
|||
$this->removedNamespaces[] = $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the session has changed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChanged()
|
||||
{
|
||||
return parent::hasChanged() || false === empty($this->namespaces) || false === empty($this->removedNamespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all values and namespaces from the session cache
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->values = array();
|
||||
$this->removed = array();
|
||||
parent::clear();
|
||||
$this->namespaces = array();
|
||||
$this->removedNamespaces = array();
|
||||
}
|
||||
|
|
|
@ -14,13 +14,6 @@ use IteratorAggregate;
|
|||
*/
|
||||
class SessionNamespace implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* The session this namespace is associated to
|
||||
*
|
||||
* @var Session
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* The actual values stored in this container
|
||||
*
|
||||
|
@ -35,16 +28,6 @@ class SessionNamespace implements IteratorAggregate
|
|||
*/
|
||||
protected $removed = array();
|
||||
|
||||
/**
|
||||
* Create a new session namespace
|
||||
*
|
||||
* @param Session $session The session this namespace is associated to
|
||||
*/
|
||||
public function __construct(Session $session = null)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator for all values in this namespace
|
||||
*
|
||||
|
@ -120,7 +103,18 @@ class SessionNamespace implements IteratorAggregate
|
|||
$this->values[$key] = $value;
|
||||
|
||||
if (in_array($key, $this->removed)) {
|
||||
unset($this->removed[array_search($key, $this->values)]);
|
||||
unset($this->removed[array_search($key, $this->removed)]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setByRef($key, &$value)
|
||||
{
|
||||
$this->values[$key] = & $value;
|
||||
|
||||
if (in_array($key, $this->removed)) {
|
||||
unset($this->removed[array_search($key, $this->removed)]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -139,6 +133,16 @@ class SessionNamespace implements IteratorAggregate
|
|||
return isset($this->values[$key]) ? $this->values[$key] : $default;
|
||||
}
|
||||
|
||||
public function & getByRef($key, $default = null)
|
||||
{
|
||||
$value = $default;
|
||||
if (isset($this->values[$key])) {
|
||||
$value = & $this->values[$key];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the given value from the session
|
||||
*
|
||||
|
@ -177,14 +181,21 @@ class SessionNamespace implements IteratorAggregate
|
|||
}
|
||||
|
||||
/**
|
||||
* Save the session this namespace is associated to
|
||||
* Return whether the session namespace has been changed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function write()
|
||||
public function hasChanged()
|
||||
{
|
||||
if (!$this->session) {
|
||||
throw new IcingaException('Cannot save, session not set');
|
||||
}
|
||||
return false === empty($this->values) || false === empty($this->removed);
|
||||
}
|
||||
|
||||
$this->session->write();
|
||||
/**
|
||||
* Clear all values from the session namespace
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->values = array();
|
||||
$this->removed = array();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class StyleSheet
|
|||
'css/icinga/main-content.less',
|
||||
'css/icinga/tabs.less',
|
||||
'css/icinga/forms.less',
|
||||
'css/icinga/setup.less',
|
||||
'css/icinga/widgets.less',
|
||||
'css/icinga/pagination.less',
|
||||
'css/icinga/monitoring-colors.less',
|
||||
|
|
|
@ -0,0 +1,522 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Web;
|
||||
|
||||
use LogicException;
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Web\Session\SessionNamespace;
|
||||
use Icinga\Web\Form\Decorator\ElementDoubler;
|
||||
|
||||
/**
|
||||
* Container and controller for form based wizards
|
||||
*/
|
||||
class Wizard
|
||||
{
|
||||
/**
|
||||
* An integer describing the wizard's forward direction
|
||||
*/
|
||||
const FORWARD = 0;
|
||||
|
||||
/**
|
||||
* An integer describing the wizard's backward direction
|
||||
*/
|
||||
const BACKWARD = 1;
|
||||
|
||||
/**
|
||||
* An integer describing that the wizard does not change its position
|
||||
*/
|
||||
const NO_CHANGE = 2;
|
||||
|
||||
/**
|
||||
* The name of the button to advance the wizard's position
|
||||
*/
|
||||
const BTN_NEXT = 'btn_next';
|
||||
|
||||
/**
|
||||
* The name of the button to rewind the wizard's position
|
||||
*/
|
||||
const BTN_PREV = 'btn_prev';
|
||||
|
||||
/**
|
||||
* The name of the wizard's current page
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $currentPage;
|
||||
|
||||
/**
|
||||
* The pages being part of this wizard
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pages = array();
|
||||
|
||||
/**
|
||||
* Initialize a new wizard
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run additional initialization routines
|
||||
*
|
||||
* Should be implemented by subclasses to add pages to the wizard.
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pages being part of this wizard
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPages()
|
||||
{
|
||||
return $this->pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the page with the given name
|
||||
*
|
||||
* @param string $name The name of the page to return
|
||||
*
|
||||
* @return null|Form The page or null in case there is no page with the given name
|
||||
*/
|
||||
public function getPage($name)
|
||||
{
|
||||
foreach ($this->getPages() as $page) {
|
||||
if ($name === $page->getName()) {
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new page to this wizard
|
||||
*
|
||||
* @param Form $page The page to add to the wizard
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addPage(Form $page)
|
||||
{
|
||||
$this->pages[] = $page;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple pages to this wizard
|
||||
*
|
||||
* @param array $pages The pages to add to the wizard
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addPages(array $pages)
|
||||
{
|
||||
foreach ($pages as $page) {
|
||||
$this->addPage($page);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that this wizard has any pages
|
||||
*
|
||||
* @throws LogicException In case this wizard has no pages
|
||||
*/
|
||||
protected function assertHasPages()
|
||||
{
|
||||
$pages = $this->getPages();
|
||||
if (count($pages) < 2) {
|
||||
throw new LogicException("Although Chuck Norris can advance a wizard with less than two pages, you can't.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current page of this wizard
|
||||
*
|
||||
* @return Form
|
||||
*
|
||||
* @throws LogicException In case the name of the current page currently being set is invalid
|
||||
*/
|
||||
public function getCurrentPage()
|
||||
{
|
||||
if ($this->currentPage === null) {
|
||||
$this->assertHasPages();
|
||||
$pages = $this->getPages();
|
||||
$this->currentPage = $this->getSession()->get('current_page', $pages[0]->getName());
|
||||
}
|
||||
|
||||
if (($page = $this->getPage($this->currentPage)) === null) {
|
||||
throw new LogicException(sprintf('No page found with name "%s"', $this->currentPage));
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current page of this wizard
|
||||
*
|
||||
* @param Form $page The page to set as current page
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCurrentPage(Form $page)
|
||||
{
|
||||
$this->currentPage = $page->getName();
|
||||
$this->getSession()->set('current_page', $this->currentPage);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the given page that is either going to be displayed or validated
|
||||
*
|
||||
* Implement this method in a subclass to populate default values and/or other data required to process the form.
|
||||
*
|
||||
* @param Form $page The page to setup
|
||||
* @param Request $request The current request
|
||||
*/
|
||||
public function setupPage(Form $page, Request $request)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the given request using this wizard
|
||||
*
|
||||
* Validate the request data using the current page, update the wizard's
|
||||
* position and redirect to the page's redirect url upon success.
|
||||
*
|
||||
* @param Request $request The request to be processed
|
||||
*
|
||||
* @return Request The request supposed to be processed
|
||||
*/
|
||||
public function handleRequest(Request $request = null)
|
||||
{
|
||||
$page = $this->getCurrentPage();
|
||||
|
||||
if ($request === null) {
|
||||
$request = $page->getRequest();
|
||||
}
|
||||
|
||||
$this->setupPage($page, $request);
|
||||
$requestData = $page->getRequestData($request);
|
||||
if ($page->wasSent($requestData)) {
|
||||
if (($requestedPage = $this->getRequestedPage($requestData)) !== null) {
|
||||
$isValid = false;
|
||||
$direction = $this->getDirection($request);
|
||||
if ($direction === static::FORWARD && $page->isValid($requestData)) {
|
||||
$isValid = true;
|
||||
if ($this->isLastPage($page)) {
|
||||
$this->setIsFinished();
|
||||
}
|
||||
} elseif ($direction === static::BACKWARD) {
|
||||
$page->populate($requestData);
|
||||
$isValid = true;
|
||||
}
|
||||
|
||||
if ($isValid) {
|
||||
$pageData = & $this->getPageData();
|
||||
$pageData[$page->getName()] = $page->getValues();
|
||||
$this->setCurrentPage($this->getNewPage($requestedPage, $page));
|
||||
$page->getResponse()->redirectAndExit($page->getRedirectUrl());
|
||||
}
|
||||
} else {
|
||||
$page->isValidPartial($requestData);
|
||||
}
|
||||
} elseif (($pageData = $this->getPageData($page->getName())) !== null) {
|
||||
$page->populate($pageData);
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the requested page
|
||||
*
|
||||
* @param array $requestData The request's data
|
||||
*
|
||||
* @return null|string The name of the requested page or null in case no page has been requested
|
||||
*/
|
||||
protected function getRequestedPage(array $requestData)
|
||||
{
|
||||
if (isset($requestData[static::BTN_NEXT])) {
|
||||
return $requestData[static::BTN_NEXT];
|
||||
} elseif (isset($requestData[static::BTN_PREV])) {
|
||||
return $requestData[static::BTN_PREV];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the direction of this wizard using the given request
|
||||
*
|
||||
* @param Request $request The request to use
|
||||
*
|
||||
* @return int The direction @see Wizard::FORWARD @see Wizard::BACKWARD @see Wizard::NO_CHANGE
|
||||
*/
|
||||
protected function getDirection(Request $request = null)
|
||||
{
|
||||
$currentPage = $this->getCurrentPage();
|
||||
|
||||
if ($request === null) {
|
||||
$request = $currentPage->getRequest();
|
||||
}
|
||||
|
||||
$requestData = $currentPage->getRequestData($request);
|
||||
if (isset($requestData[static::BTN_NEXT])) {
|
||||
return static::FORWARD;
|
||||
} elseif (isset($requestData[static::BTN_PREV])) {
|
||||
return static::BACKWARD;
|
||||
}
|
||||
|
||||
return static::NO_CHANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new page to set as current page
|
||||
*
|
||||
* Permission is checked by verifying that the requested page's previous page has page data available.
|
||||
* The requested page is automatically permitted without any checks if the origin page is its previous
|
||||
* page or one that occurs later in order.
|
||||
*
|
||||
* @param string $requestedPage The name of the requested page
|
||||
* @param Form $originPage The origin page
|
||||
*
|
||||
* @return Form The new page
|
||||
*
|
||||
* @throws InvalidArgumentException In case the requested page does not exist or is not permitted yet
|
||||
*/
|
||||
protected function getNewPage($requestedPage, Form $originPage)
|
||||
{
|
||||
if (($page = $this->getPage($requestedPage)) !== null) {
|
||||
$permitted = true;
|
||||
|
||||
$pages = $this->getPages();
|
||||
if (($index = array_search($page, $pages, true)) > 0) {
|
||||
$previousPage = $pages[$index - 1];
|
||||
if ($originPage === null || ($previousPage->getName() !== $originPage->getName()
|
||||
&& array_search($originPage, $pages, true) < $index))
|
||||
{
|
||||
$permitted = $this->hasPageData($previousPage->getName());
|
||||
}
|
||||
}
|
||||
|
||||
if ($permitted) {
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
sprintf('"%s" is either an unknown page or one you are not permitted to view', $requestedPage)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given page is this wizard's last page
|
||||
*
|
||||
* @param Form $page The page to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLastPage(Form $page)
|
||||
{
|
||||
$pages = $this->getPages();
|
||||
return $page->getName() === end($pages)->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this wizard has been completed
|
||||
*
|
||||
* @param bool $state Whether this wizard has been completed
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setIsFinished($state = true)
|
||||
{
|
||||
$this->getSession()->set('isFinished', $state);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this wizard has been completed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFinished()
|
||||
{
|
||||
return $this->getSession()->get('isFinished', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the overall page data or one for a particular page
|
||||
*
|
||||
* Note that this method returns by reference so in order to update the
|
||||
* returned array set this method's return value also by reference.
|
||||
*
|
||||
* @param string $pageName The page for which to return the data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function & getPageData($pageName = null)
|
||||
{
|
||||
$session = $this->getSession();
|
||||
|
||||
if (false === isset($session->page_data)) {
|
||||
$session->page_data = array();
|
||||
}
|
||||
|
||||
$pageData = & $session->getByRef('page_data');
|
||||
if ($pageName !== null) {
|
||||
$data = null;
|
||||
if (isset($pageData[$pageName])) {
|
||||
$data = & $pageData[$pageName];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return $pageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether there is any data for the given page
|
||||
*
|
||||
* @param string $pageName The name of the page to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPageData($pageName)
|
||||
{
|
||||
return $this->getPageData($pageName) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a session to be used by this wizard
|
||||
*
|
||||
* @return SessionNamespace
|
||||
*/
|
||||
public function getSession()
|
||||
{
|
||||
return Session::getSession()->getNamespace(get_class($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the session being used by this wizard
|
||||
*/
|
||||
public function clearSession()
|
||||
{
|
||||
$this->getSession()->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add buttons to the given page based on its position in the page-chain
|
||||
*
|
||||
* @param Form $page The page to add the buttons to
|
||||
*/
|
||||
protected function addButtons(Form $page)
|
||||
{
|
||||
$pages = $this->getPages();
|
||||
$index = array_search($page, $pages, true);
|
||||
if ($index === 0) {
|
||||
$page->addElement(
|
||||
'button',
|
||||
static::BTN_NEXT,
|
||||
array(
|
||||
'type' => 'submit',
|
||||
'value' => $pages[1]->getName(),
|
||||
'label' => t('Next'),
|
||||
'decorators' => array('ViewHelper')
|
||||
)
|
||||
);
|
||||
} elseif ($index < count($pages) - 1) {
|
||||
$page->addElement(
|
||||
'button',
|
||||
static::BTN_PREV,
|
||||
array(
|
||||
'type' => 'submit',
|
||||
'value' => $pages[$index - 1]->getName(),
|
||||
'label' => t('Back'),
|
||||
'decorators' => array('ViewHelper')
|
||||
)
|
||||
);
|
||||
$page->addElement(
|
||||
'button',
|
||||
static::BTN_NEXT,
|
||||
array(
|
||||
'type' => 'submit',
|
||||
'value' => $pages[$index + 1]->getName(),
|
||||
'label' => t('Next'),
|
||||
'decorators' => array('ViewHelper')
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$page->addElement(
|
||||
'button',
|
||||
static::BTN_PREV,
|
||||
array(
|
||||
'type' => 'submit',
|
||||
'value' => $pages[$index - 1]->getName(),
|
||||
'label' => t('Back'),
|
||||
'decorators' => array('ViewHelper')
|
||||
)
|
||||
);
|
||||
$page->addElement(
|
||||
'button',
|
||||
static::BTN_NEXT,
|
||||
array(
|
||||
'type' => 'submit',
|
||||
'value' => $page->getName(),
|
||||
'label' => t('Finish'),
|
||||
'decorators' => array('ViewHelper')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$page->addDisplayGroup(
|
||||
array(static::BTN_PREV, static::BTN_NEXT),
|
||||
'buttons',
|
||||
array(
|
||||
'decorators' => array(
|
||||
'FormElements',
|
||||
new ElementDoubler(array(
|
||||
'double' => static::BTN_NEXT,
|
||||
'condition' => static::BTN_PREV,
|
||||
'placement' => ElementDoubler::PREPEND,
|
||||
'attributes' => array('tabindex' => -1, 'class' => 'double')
|
||||
)),
|
||||
array('HtmlTag', array('tag' => 'div', 'class' => 'buttons'))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current page of this wizard with appropriate buttons being added
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm()
|
||||
{
|
||||
$form = $this->getCurrentPage();
|
||||
$form->create(); // Make sure that buttons are displayed at the very bottom
|
||||
$this->addButtons($form);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current page of this wizard rendered as HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getForm();
|
||||
}
|
||||
}
|
|
@ -55,7 +55,8 @@ class SecurityConfigForm extends ConfigForm
|
|||
'text',
|
||||
'protected_customvars',
|
||||
array(
|
||||
'required' => true,
|
||||
'allowEmpty' => true,
|
||||
'value' => '*pw*,*pass*,community',
|
||||
'label' => mt('monitoring', 'Protected Custom Variables'),
|
||||
'description' => mt('monitoring',
|
||||
'Comma separated case insensitive list of protected custom variables.'
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring\Form\Setup;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Application\Platform;
|
||||
|
||||
class BackendPage extends Form
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_monitoring_backend');
|
||||
}
|
||||
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('monitoring', 'Monitoring Backend', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'Please configure below how Icinga Web 2 should retrieve monitoring information.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'icinga',
|
||||
'label' => mt('monitoring', 'Backend Name'),
|
||||
'description' => mt('monitoring', 'The identifier of this backend')
|
||||
)
|
||||
);
|
||||
|
||||
$resourceTypes = array();
|
||||
if (Platform::extensionLoaded('mysql') || Platform::extensionLoaded('pgsql')) {
|
||||
$resourceTypes['ido'] = 'IDO';
|
||||
}
|
||||
$resourceTypes['livestatus'] = 'Livestatus';
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => mt('monitoring', 'Backend Type'),
|
||||
'description' => mt('monitoring', 'The data source used for retrieving monitoring information'),
|
||||
'multiOptions' => $resourceTypes
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring\Form\Setup;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\Config\Resource\DbResourceForm;
|
||||
|
||||
class IdoResourcePage extends Form
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_monitoring_ido');
|
||||
}
|
||||
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'db'
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('monitoring', 'Monitoring IDO Resource', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'Please fill out the connection details below to access'
|
||||
. ' the IDO database of your monitoring environment.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
} else {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$livestatusResourceForm = new DbResourceForm();
|
||||
$this->addElements($livestatusResourceForm->createElements($formData)->getElements());
|
||||
$this->getElement('name')->setValue('icinga_ido');
|
||||
}
|
||||
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
|
||||
if (false === DbResourceForm::isValidResource($this)) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to the form by which the user can skip the connection validation
|
||||
*/
|
||||
protected function addSkipValidationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Skip Validation'),
|
||||
'description' => t('Check this to not to validate connectivity with the given database server')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring\Form\Setup;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Module\Monitoring\Form\Config\InstanceConfigForm;
|
||||
|
||||
class InstancePage extends Form
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_monitoring_instance');
|
||||
}
|
||||
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('monitoring', 'Monitoring Instance', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'Please define the settings specific to your monitoring instance below.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($formData['host'])) {
|
||||
$formData['type'] = 'remote'; // This is necessary as the type element gets ignored by Form::getValues()
|
||||
}
|
||||
|
||||
$instanceConfigForm = new InstanceConfigForm();
|
||||
$instanceConfigForm->createElements($formData);
|
||||
$this->addElements($instanceConfigForm->getElements());
|
||||
$this->getElement('name')->setValue('icinga');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring\Form\Setup;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\Config\Resource\LivestatusResourceForm;
|
||||
|
||||
class LivestatusResourcePage extends Form
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_monitoring_livestatus');
|
||||
}
|
||||
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'livestatus'
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('monitoring', 'Monitoring Livestatus Resource', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'Please fill out the connection details below to access the Livestatus'
|
||||
. ' socket interface for your monitoring environment.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
} else {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$livestatusResourceForm = new LivestatusResourceForm();
|
||||
$this->addElements($livestatusResourceForm->createElements($formData)->getElements());
|
||||
$this->getElement('name')->setValue('icinga_livestatus');
|
||||
}
|
||||
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
|
||||
if (false === LivestatusResourceForm::isValidResource($this)) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to the form by which the user can skip the connection validation
|
||||
*/
|
||||
protected function addSkipValidationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('Skip Validation'),
|
||||
'description' => t('Check this to not to validate connectivity with the given Livestatus socket')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring\Form\Setup;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Module\Monitoring\Form\Config\SecurityConfigForm;
|
||||
|
||||
class SecurityPage extends Form
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_monitoring_security');
|
||||
}
|
||||
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('monitoring', 'Monitoring Security', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'To protect your monitoring environment against prying eyes please fill out the settings below.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$securityConfigForm = new SecurityConfigForm();
|
||||
$securityConfigForm->createElements($formData);
|
||||
$this->addElements($securityConfigForm->getElements());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring\Form\Setup;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
|
||||
class WelcomePage extends Form
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_monitoring_welcome');
|
||||
}
|
||||
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'welcome',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'Welcome to the configuration of the monitoring module for Icinga Web 2!'
|
||||
),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'core_hint',
|
||||
array(
|
||||
'value' => mt('monitoring', 'This is the core module for Icinga Web 2.')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'monitoring',
|
||||
'It offers various status and reporting views with powerful filter capabilities that allow'
|
||||
. ' you to keep track of the most important events in your monitoring environment.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addDisplayGroup(
|
||||
array('core_hint', 'description'),
|
||||
'info',
|
||||
array(
|
||||
'decorators' => array(
|
||||
'FormElements',
|
||||
array('HtmlTag', array('tag' => 'div', 'class' => 'info'))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ $this->provideConfigTab('security', array(
|
|||
'title' => 'Security',
|
||||
'url' => 'config/security'
|
||||
));
|
||||
$this->provideSetupWizard('Icinga\Module\Monitoring\MonitoringWizard');
|
||||
|
||||
/*
|
||||
* Available Search Urls
|
||||
|
|
|
@ -655,7 +655,6 @@ abstract class IdoQuery extends DbQuery
|
|||
);
|
||||
if ($session !== null) {
|
||||
$session->version = self::$idoVersion;
|
||||
$session->write(); // <- WHY? I don't want to care about this!
|
||||
}
|
||||
}
|
||||
return self::$idoVersion;
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
|
||||
class BackendStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $backendIniError;
|
||||
|
||||
protected $resourcesIniError;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$success = $this->createBackendsIni();
|
||||
$success &= $this->createResourcesIni();
|
||||
return $success;
|
||||
}
|
||||
|
||||
protected function createBackendsIni()
|
||||
{
|
||||
$config = array();
|
||||
$config[$this->data['backendConfig']['name']] = array(
|
||||
'type' => $this->data['backendConfig']['type'],
|
||||
'resource' => $this->data['resourceConfig']['name']
|
||||
);
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config($config),
|
||||
'filename' => Config::resolvePath('modules/monitoring/backends.ini')
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->backendIniError = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->backendIniError = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function createResourcesIni()
|
||||
{
|
||||
$resourceConfig = $this->data['resourceConfig'];
|
||||
$resourceName = $resourceConfig['name'];
|
||||
unset($resourceConfig['name']);
|
||||
|
||||
try {
|
||||
$config = Config::app('resources', true);
|
||||
$config->merge(new Config(array($resourceName => $resourceConfig)));
|
||||
|
||||
$writer = new IniWriter(array(
|
||||
'config' => $config,
|
||||
'filename' => Config::resolvePath('resources.ini'),
|
||||
'filemode' => 0660
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->resourcesIniError = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->resourcesIniError = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$pageTitle = '<h2>' . mt('monitoring', 'Monitoring Backend', 'setup.page.title') . '</h2>';
|
||||
$backendDescription = '<p>' . sprintf(
|
||||
mt(
|
||||
'monitoring',
|
||||
'Icinga Web 2 will retrieve information from your monitoring environment'
|
||||
. ' using a backend called "%s" and the specified resource below:'
|
||||
),
|
||||
$this->data['backendConfig']['name']
|
||||
) . '</p>';
|
||||
|
||||
if ($this->data['resourceConfig']['type'] === 'db') {
|
||||
$resourceTitle = '<h3>' . mt('monitoring', 'Database Resource') . '</h3>';
|
||||
$resourceHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Resource Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['name'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Database Type') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['db'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Host') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['host'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Port') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['port'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Database Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['dbname'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Username') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['username'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Password') . '</strong></td>'
|
||||
. '<td>' . str_repeat('*', strlen($this->data['resourceConfig']['password'])) . '</td>'
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
} else { // $this->data['resourceConfig']['type'] === 'livestatus'
|
||||
$resourceTitle = '<h3>' . mt('monitoring', 'Livestatus Resource') . '</h3>';
|
||||
$resourceHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Resource Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['name'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Socket') . '</strong></td>'
|
||||
. '<td>' . $this->data['resourceConfig']['socket'] . '</td>'
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
}
|
||||
|
||||
return $pageTitle . '<div class="topic">' . $backendDescription . $resourceTitle . $resourceHtml . '</div>';
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
$report = '';
|
||||
if ($this->backendIniError === false) {
|
||||
$message = mt('monitoring', 'Monitoring backend configuration has been successfully written to: %s');
|
||||
$report .= '<p>' . sprintf($message, Config::resolvePath('modules/monitoring/backends.ini')) . '</p>';
|
||||
} elseif ($this->backendIniError !== null) {
|
||||
$message = mt(
|
||||
'monitoring',
|
||||
'Monitoring backend configuration could not be written to: %s; An error occured:'
|
||||
);
|
||||
$report .= '<p class="error">' . sprintf(
|
||||
$message,
|
||||
Config::resolvePath('modules/monitoring/backends.ini')
|
||||
) . '</p><p>' . $this->backendIniError->getMessage() . '</p>';
|
||||
}
|
||||
|
||||
if ($this->resourcesIniError === false) {
|
||||
$message = mt('monitoring', 'Resource configuration has been successfully updated: %s');
|
||||
$report .= '<p>' . sprintf($message, Config::resolvePath('resources.ini')) . '</p>';
|
||||
} elseif ($this->resourcesIniError !== null) {
|
||||
$message = mt('monitoring', 'Resource configuration could not be udpated: %s; An error occured:');
|
||||
$report .= '<p class="error">' . sprintf($message, Config::resolvePath('resources.ini')) . '</p>'
|
||||
. '<p>' . $this->resourcesIniError->getMessage() . '</p>';
|
||||
}
|
||||
|
||||
return $report;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
|
||||
class InstanceStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $error;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$instanceConfig = $this->data['instanceConfig'];
|
||||
$instanceName = $instanceConfig['name'];
|
||||
unset($instanceConfig['name']);
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config(array($instanceName => $instanceConfig)),
|
||||
'filename' => Config::resolvePath('modules/monitoring/instances.ini')
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->error = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$pageTitle = '<h2>' . mt('monitoring', 'Monitoring Instance', 'setup.page.title') . '</h2>';
|
||||
|
||||
if (isset($this->data['instanceConfig']['host'])) {
|
||||
$pipeHtml = '<p>' . sprintf(
|
||||
mt(
|
||||
'monitoring',
|
||||
'Icinga Web 2 will use the named pipe located on a remote machine at "%s" to send commands'
|
||||
. ' to your monitoring instance by using the connection details listed below:'
|
||||
),
|
||||
$this->data['instanceConfig']['path']
|
||||
) . '</p>';
|
||||
|
||||
$pipeHtml .= ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . mt('monitoring', 'Remote Host') . '</strong></td>'
|
||||
. '<td>' . $this->data['instanceConfig']['host'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . mt('monitoring', 'Remote SSH Port') . '</strong></td>'
|
||||
. '<td>' . $this->data['instanceConfig']['port'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . mt('monitoring', 'Remote SSH User') . '</strong></td>'
|
||||
. '<td>' . $this->data['instanceConfig']['user'] . '</td>'
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
} else {
|
||||
$pipeHtml = '<p>' . sprintf(
|
||||
mt(
|
||||
'monitoring',
|
||||
'Icinga Web 2 will use the named pipe located at "%s"'
|
||||
. ' to send commands to your monitoring instance.'
|
||||
),
|
||||
$this->data['instanceConfig']['path']
|
||||
) . '</p>';
|
||||
}
|
||||
|
||||
return $pageTitle . '<div class="topic">' . $pipeHtml . '</div>';
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
if ($this->error === false) {
|
||||
$message = mt('monitoring', 'Monitoring instance configuration has been successfully created: %s');
|
||||
return '<p>' . sprintf($message, Config::resolvePath('modules/monitoring/instances.ini')) . '</p>';
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt(
|
||||
'monitoring',
|
||||
'Monitoring instance configuration could not be written to: %s; An error occured:'
|
||||
);
|
||||
return '<p class="error">' . sprintf($message, Config::resolvePath('modules/monitoring/instances.ini'))
|
||||
. '</p><p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Wizard;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Module\Setup\Setup;
|
||||
use Icinga\Module\Setup\SetupWizard;
|
||||
use Icinga\Module\Setup\Requirements;
|
||||
use Icinga\Module\Setup\Utils\MakeDirStep;
|
||||
use Icinga\Module\Setup\Utils\EnableModuleStep;
|
||||
use Icinga\Module\Setup\Form\SummaryPage;
|
||||
use Icinga\Module\Monitoring\Form\Setup\WelcomePage;
|
||||
use Icinga\Module\Monitoring\Form\Setup\BackendPage;
|
||||
use Icinga\Module\Monitoring\Form\Setup\InstancePage;
|
||||
use Icinga\Module\Monitoring\Form\Setup\SecurityPage;
|
||||
use Icinga\Module\Monitoring\Form\Setup\IdoResourcePage;
|
||||
use Icinga\Module\Monitoring\Form\Setup\LivestatusResourcePage;
|
||||
|
||||
/**
|
||||
* Monitoring Module Setup Wizard
|
||||
*/
|
||||
class MonitoringWizard extends Wizard implements SetupWizard
|
||||
{
|
||||
/**
|
||||
* @see Wizard::init()
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addPage(new WelcomePage());
|
||||
$this->addPage(new BackendPage());
|
||||
$this->addPage(new IdoResourcePage());
|
||||
$this->addPage(new LivestatusResourcePage());
|
||||
$this->addPage(new InstancePage());
|
||||
$this->addPage(new SecurityPage());
|
||||
$this->addPage(new SummaryPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::setupPage()
|
||||
*/
|
||||
public function setupPage(Form $page, Request $request)
|
||||
{
|
||||
if ($page->getName() === 'setup_requirements') {
|
||||
$page->setRequirements($this->getRequirements());
|
||||
} elseif ($page->getName() === 'setup_summary') {
|
||||
$page->setSummary($this->getSetup()->getSummary());
|
||||
$page->setSubjectTitle(mt('monitoring', 'the monitoring module', 'setup.summary.subject'));
|
||||
} elseif (
|
||||
$this->getDirection() === static::FORWARD
|
||||
&& ($page->getName() === 'setup_monitoring_ido' || $page->getName() === 'setup_monitoring_livestatus')
|
||||
) {
|
||||
if ((($dbResourceData = $this->getPageData('setup_db_resource')) !== null
|
||||
&& $dbResourceData['name'] === $request->getPost('name'))
|
||||
|| (($ldapResourceData = $this->getPageData('setup_ldap_resource')) !== null
|
||||
&& $ldapResourceData['name'] === $request->getPost('name'))
|
||||
) {
|
||||
$page->addError(mt('monitoring', 'The given resource name is already in use.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::getNewPage()
|
||||
*/
|
||||
protected function getNewPage($requestedPage, Form $originPage)
|
||||
{
|
||||
$skip = false;
|
||||
$newPage = parent::getNewPage($requestedPage, $originPage);
|
||||
if ($newPage->getName() === 'setup_monitoring_ido') {
|
||||
$backendData = $this->getPageData('setup_monitoring_backend');
|
||||
$skip = $backendData['type'] !== 'ido';
|
||||
} elseif ($newPage->getName() === 'setup_monitoring_livestatus') {
|
||||
$backendData = $this->getPageData('setup_monitoring_backend');
|
||||
$skip = $backendData['type'] !== 'livestatus';
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
if ($this->hasPageData($newPage->getName())) {
|
||||
$pageData = & $this->getPageData();
|
||||
unset($pageData[$newPage->getName()]);
|
||||
}
|
||||
|
||||
$pages = $this->getPages();
|
||||
if ($this->getDirection() === static::FORWARD) {
|
||||
$nextPage = $pages[array_search($newPage, $pages, true) + 1];
|
||||
$newPage = $this->getNewPage($nextPage->getName(), $newPage);
|
||||
} else { // $this->getDirection() === static::BACKWARD
|
||||
$previousPage = $pages[array_search($newPage, $pages, true) - 1];
|
||||
$newPage = $this->getNewPage($previousPage->getName(), $newPage);
|
||||
}
|
||||
}
|
||||
|
||||
return $newPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::addButtons()
|
||||
*/
|
||||
protected function addButtons(Form $page)
|
||||
{
|
||||
parent::addButtons($page);
|
||||
|
||||
$pages = $this->getPages();
|
||||
$index = array_search($page, $pages, true);
|
||||
if ($index === 0) {
|
||||
// Used t() here as "Start" is too generic and already translated in the icinga domain
|
||||
$page->getElement(static::BTN_NEXT)->setLabel(t('Start', 'setup.welcome.btn.next'));
|
||||
} elseif ($index === count($pages) - 1) {
|
||||
$page->getElement(static::BTN_NEXT)->setLabel(
|
||||
mt('monitoring', 'Setup the monitoring module for Icinga Web 2', 'setup.summary.btn.finish')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getSetup()
|
||||
*/
|
||||
public function getSetup()
|
||||
{
|
||||
$pageData = $this->getPageData();
|
||||
$setup = new Setup();
|
||||
|
||||
$setup->addStep(new MakeDirStep(array($this->getConfigDir() . '/modules/monitoring'), 0775));
|
||||
|
||||
$setup->addStep(
|
||||
new BackendStep(array(
|
||||
'backendConfig' => $pageData['setup_monitoring_backend'],
|
||||
'resourceConfig' => isset($pageData['setup_monitoring_ido'])
|
||||
? array_diff_key($pageData['setup_monitoring_ido'], array('skip_validation' => null))
|
||||
: array_diff_key($pageData['setup_monitoring_livestatus'], array('skip_validation' => null))
|
||||
))
|
||||
);
|
||||
|
||||
$setup->addStep(
|
||||
new InstanceStep(array(
|
||||
'instanceConfig' => $pageData['setup_monitoring_instance']
|
||||
))
|
||||
);
|
||||
|
||||
$setup->addStep(
|
||||
new SecurityStep(array(
|
||||
'securityConfig' => $pageData['setup_monitoring_security']
|
||||
))
|
||||
);
|
||||
|
||||
$setup->addStep(new EnableModuleStep('monitoring'));
|
||||
|
||||
return $setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getRequirements()
|
||||
*/
|
||||
public function getRequirements()
|
||||
{
|
||||
return new Requirements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration directory of Icinga Web 2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigDir()
|
||||
{
|
||||
if (array_key_exists('ICINGAWEB_CONFIGDIR', $_SERVER)) {
|
||||
$configDir = $_SERVER['ICINGAWEB_CONFIGDIR'];
|
||||
} else {
|
||||
$configDir = '/etc/icingaweb';
|
||||
}
|
||||
|
||||
$canonical = realpath($configDir);
|
||||
return $canonical ? $canonical : $configDir;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Monitoring;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
|
||||
class SecurityStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $error;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$config = array();
|
||||
$config['security'] = $this->data['securityConfig'];
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config($config),
|
||||
'filename' => Config::resolvePath('modules/monitoring/config.ini')
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->error = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$pageTitle = '<h2>' . mt('monitoring', 'Monitoring Security', 'setup.page.title') . '</h2>';
|
||||
$pageDescription = '<p>' . mt(
|
||||
'monitoring',
|
||||
'Icinga Web 2 will protect your monitoring environment against'
|
||||
. ' prying eyes using the configuration specified below:'
|
||||
) . '</p>';
|
||||
|
||||
$pageHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . mt('monitoring', 'Protected Custom Variables') . '</strong></td>'
|
||||
. '<td>' . ($this->data['securityConfig']['protected_customvars'] ? (
|
||||
$this->data['securityConfig']['protected_customvars']
|
||||
) : mt('monitoring', 'None', 'monitoring.protected_customvars')) . '</td>'
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
|
||||
return $pageTitle . '<div class="topic">' . $pageDescription . $pageHtml . '</div>';
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
if ($this->error === false) {
|
||||
$message = mt('monitoring', 'Monitoring security configuration has been successfully created: %s');
|
||||
return '<p>' . sprintf($message, Config::resolvePath('modules/monitoring/config.ini')) . '</p>';
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt(
|
||||
'monitoring',
|
||||
'Monitoring security configuration could not be written to: %s; An error occured:'
|
||||
);
|
||||
return '<p class="error">' . sprintf($message, Config::resolvePath('modules/monitoring/config.ini'))
|
||||
. '</p><p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -288,7 +288,6 @@ class TimeLine implements IteratorAggregate
|
|||
|
||||
if ($this->session !== null) {
|
||||
$this->session->calculationBase = $new;
|
||||
$this->session->write();
|
||||
}
|
||||
} else {
|
||||
$this->calculationBase = $calculationBase;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Clicommands;
|
||||
|
||||
use Icinga\Cli\Command;
|
||||
|
||||
class ConfigCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Create the configuration directory
|
||||
*
|
||||
* This command creates the configuration directory for Icinga Web 2. The `group' argument
|
||||
* is mandatory and should be the groupname of the user your web server is running as.
|
||||
*
|
||||
* USAGE:
|
||||
*
|
||||
* icingacli setup config createDirectory <group> [options]
|
||||
*
|
||||
* OPTIONS:
|
||||
*
|
||||
* --mode The access mode to use. Default is: 2770
|
||||
* --path The path to the configuration directory. If omitted the default is used.
|
||||
*
|
||||
* EXAMPLES:
|
||||
*
|
||||
* icingacli setup config createDirectory apache
|
||||
* icingacli setup config createDirectory apache --mode 2775
|
||||
* icingacli setup config createDirectory apache --path /some/path
|
||||
*/
|
||||
public function createDirectoryAction()
|
||||
{
|
||||
$group = $this->params->getStandalone();
|
||||
if ($group === null) {
|
||||
$this->fail($this->translate('The `group\' argument is mandatory.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = $this->params->get('path', $this->app->getConfigDir());
|
||||
if (file_exists($path)) {
|
||||
printf($this->translate("Configuration directory already exists at: %s\n"), $path);
|
||||
return true;
|
||||
}
|
||||
|
||||
$mode = octdec($this->params->get('mode', '2770'));
|
||||
if (false === mkdir($path)) {
|
||||
$this->fail(sprintf($this->translate('Unable to create path: %s'), $path));
|
||||
return false;
|
||||
}
|
||||
|
||||
$old = umask(0); // Prevent $mode from being mangled by the system's umask ($old)
|
||||
chmod($path, $mode);
|
||||
umask($old);
|
||||
|
||||
if (chgrp($path, $group) === false) {
|
||||
$this->fail(sprintf($this->translate('Unable to change the group of "%s" to "%s".'), $path, $group));
|
||||
return false;
|
||||
}
|
||||
|
||||
printf($this->translate("Successfully created configuration directory at: %s\n"), $path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Clicommands;
|
||||
|
||||
use Icinga\Cli\Command;
|
||||
|
||||
/**
|
||||
* Maintain the setup wizard's authentication
|
||||
*
|
||||
* The token command allows you to display the current setup token or to create a new one.
|
||||
*
|
||||
* Usage: icingacli setup token <action>
|
||||
*/
|
||||
class TokenCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Display the current setup token
|
||||
*
|
||||
* Shows you the current setup token used to authenticate when setting up Icinga Web 2 using the web-based wizard.
|
||||
*
|
||||
* USAGE:
|
||||
*
|
||||
* icingacli setup token show
|
||||
*/
|
||||
public function showAction()
|
||||
{
|
||||
$token = file_get_contents($this->app->getConfigDir() . '/setup.token');
|
||||
if (! $token) {
|
||||
$this->fail(
|
||||
$this->translate('Nothing to show. Please create a new setup token using the generateToken action.')
|
||||
);
|
||||
}
|
||||
|
||||
printf($this->translate("The current setup token is: %s\n"), $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new setup token
|
||||
*
|
||||
* Re-generates the setup token used to authenticate when setting up Icinga Web 2 using the web-based wizard.
|
||||
*
|
||||
* USAGE:
|
||||
*
|
||||
* icingacli setup token create
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$token = bin2hex(openssl_random_pseudo_bytes(8));
|
||||
} else {
|
||||
$token = substr(md5(mt_rand()), 16);
|
||||
}
|
||||
|
||||
$filepath = $this->app->getConfigDir() . '/setup.token';
|
||||
|
||||
if (false === file_put_contents($filepath, $token)) {
|
||||
$this->fail(sprintf($this->translate('Cannot write setup token "%s" to disk.'), $filepath));
|
||||
}
|
||||
|
||||
if (false === chmod($filepath, 0660)) {
|
||||
$this->fail(sprintf($this->translate('Cannot change access mode of "%s" to %o.'), $filepath, 0660));
|
||||
}
|
||||
|
||||
printf($this->translate("The newly generated setup token is: %s\n"), $token);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use Icinga\Web\Controller\ModuleActionController;
|
||||
use Icinga\Module\Setup\WebWizard;
|
||||
|
||||
class Setup_IndexController extends ModuleActionController
|
||||
{
|
||||
/**
|
||||
* Whether the controller requires the user to be authenticated
|
||||
*
|
||||
* FALSE as the wizard uses token authentication
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $requiresAuthentication = false;
|
||||
|
||||
/**
|
||||
* Show the web wizard and run the configuration once finished
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$wizard = new WebWizard();
|
||||
|
||||
if ($wizard->isFinished()) {
|
||||
$setup = $wizard->getSetup();
|
||||
$success = $setup->run();
|
||||
if ($success) {
|
||||
$wizard->clearSession();
|
||||
} else {
|
||||
$wizard->setIsFinished(false);
|
||||
}
|
||||
|
||||
$this->view->success = $success;
|
||||
$this->view->report = $setup->getReport();
|
||||
} else {
|
||||
$wizard->handleRequest();
|
||||
}
|
||||
|
||||
$this->view->wizard = $wizard;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Exception;
|
||||
use LogicException;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Authentication\Backend\DbUserBackend;
|
||||
use Icinga\Authentication\Backend\LdapUserBackend;
|
||||
|
||||
/**
|
||||
* Wizard page to define the initial administrative account
|
||||
*/
|
||||
class AdminAccountPage extends Form
|
||||
{
|
||||
/**
|
||||
* The resource configuration to use
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $resourceConfig;
|
||||
|
||||
/**
|
||||
* The backend configuration to use
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $backendConfig;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_admin_account');
|
||||
$this->setViewScript('form/setup-admin-account.phtml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource configuration to use
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setResourceConfig(array $config)
|
||||
{
|
||||
$this->resourceConfig = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the backend configuration to use
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setBackendConfig(array $config)
|
||||
{
|
||||
$this->backendConfig = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$choices = array();
|
||||
|
||||
if ($this->backendConfig['backend'] !== 'db') {
|
||||
$choices['by_name'] = mt('setup', 'By Name', 'setup.admin');
|
||||
$this->addElement(
|
||||
'text',
|
||||
'by_name',
|
||||
array(
|
||||
'required' => isset($formData['user_type']) && $formData['user_type'] === 'by_name',
|
||||
'value' => $this->getUsername(),
|
||||
'label' => mt('setup', 'Username'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Define the initial administrative account by providing a username that reflects'
|
||||
. ' a user created later or one that is authenticated using external mechanisms'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->backendConfig['backend'] === 'db' || $this->backendConfig['backend'] === 'ldap') {
|
||||
$users = $this->fetchUsers();
|
||||
if (false === empty($users)) {
|
||||
$choices['existing_user'] = mt('setup', 'Existing User');
|
||||
$this->addElement(
|
||||
'select',
|
||||
'existing_user',
|
||||
array(
|
||||
'required' => isset($formData['user_type']) && $formData['user_type'] === 'existing_user',
|
||||
'label' => mt('setup', 'Username'),
|
||||
'description' => sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'Choose a user reported by the %s backend as the initial administrative account',
|
||||
'setup.admin'
|
||||
),
|
||||
$this->backendConfig['backend'] === 'db'
|
||||
? mt('setup', 'database', 'setup.admin.authbackend')
|
||||
: 'LDAP'
|
||||
),
|
||||
'multiOptions' => array_combine($users, $users)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->backendConfig['backend'] === 'db') {
|
||||
$choices['new_user'] = mt('setup', 'New User');
|
||||
$required = isset($formData['user_type']) && $formData['user_type'] === 'new_user';
|
||||
$this->addElement(
|
||||
'text',
|
||||
'new_user',
|
||||
array(
|
||||
'required' => $required,
|
||||
'label' => mt('setup', 'Username'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Enter the username to be used when creating an initial administrative account'
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'password',
|
||||
'new_user_password',
|
||||
array(
|
||||
'required' => $required,
|
||||
'label' => mt('setup', 'Password'),
|
||||
'description' => mt('setup', 'Enter the password to assign to the newly created account')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'password',
|
||||
'new_user_2ndpass',
|
||||
array(
|
||||
'required' => $required,
|
||||
'label' => mt('setup', 'Repeat password'),
|
||||
'description' => mt('setup', 'Please repeat the password given above to avoid typing errors'),
|
||||
'validators' => array(
|
||||
array('identical', false, array('new_user_password'))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (count($choices) > 1) {
|
||||
$this->addElement(
|
||||
'radio',
|
||||
'user_type',
|
||||
array(
|
||||
'required' => true,
|
||||
'multiOptions' => $choices
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'user_type',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => key($choices)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Administration', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => tp(
|
||||
'Now it\'s time to configure your first administrative account for Icinga Web 2.'
|
||||
. ' Please follow the instructions below:',
|
||||
'Now it\'s time to configure your first administrative account for Icinga Web 2.'
|
||||
. ' Below are several options you can choose from. Select one and follow its instructions:',
|
||||
count($choices)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given request data and ensure that any new user does not already exist
|
||||
*
|
||||
* @param array $data The request data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($data['user_type'] === 'new_user' && array_search($data['new_user'], $this->fetchUsers()) !== false) {
|
||||
$this->getElement('new_user')->addError(mt('setup', 'Username already exists.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the externally authenticated user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUsername()
|
||||
{
|
||||
if (false === isset($_SERVER['REMOTE_USER'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$name = $_SERVER['REMOTE_USER'];
|
||||
if (isset($this->backendConfig['strip_username_regexp']) && $this->backendConfig['strip_username_regexp']) {
|
||||
// No need to silence or log anything here because the pattern has
|
||||
// already been successfully compiled during backend configuration
|
||||
$name = preg_replace($this->backendConfig['strip_username_regexp'], '', $name);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of all users this backend currently provides
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws LogicException In case the backend to fetch users from is not supported
|
||||
*/
|
||||
protected function fetchUsers()
|
||||
{
|
||||
if ($this->backendConfig['backend'] === 'db') {
|
||||
$backend = new DbUserBackend(ResourceFactory::createResource(new Config($this->resourceConfig)));
|
||||
} elseif ($this->backendConfig['backend'] === 'ldap') {
|
||||
$backend = new LdapUserBackend(
|
||||
ResourceFactory::createResource(new Config($this->resourceConfig)),
|
||||
$this->backendConfig['user_class'],
|
||||
$this->backendConfig['user_name_attribute'],
|
||||
$this->backendConfig['base_dn']
|
||||
);
|
||||
} else {
|
||||
throw new LogicException(
|
||||
sprintf(
|
||||
'Tried to fetch users from an unsupported authentication backend: %s',
|
||||
$this->backendConfig['backend']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return $backend->listUsers();
|
||||
} catch (Exception $e) {
|
||||
// No need to handle anything special here. Error means no users found.
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\Config\Authentication\DbBackendForm;
|
||||
use Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||
use Icinga\Form\Config\Authentication\AutologinBackendForm;
|
||||
|
||||
/**
|
||||
* Wizard page to define authentication backend specific details
|
||||
*/
|
||||
class AuthBackendPage extends Form
|
||||
{
|
||||
/**
|
||||
* The resource configuration to use
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_authentication_backend');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource configuration to use
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setResourceConfig(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource configuration as Config object
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getResourceConfig()
|
||||
{
|
||||
return new Config($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Authentication Backend', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->config['type'] === 'db') {
|
||||
$note = mt(
|
||||
'setup',
|
||||
'As you\'ve chosen to use a database for authentication all you need '
|
||||
. 'to do now is defining a name for your first authentication backend.'
|
||||
);
|
||||
} elseif ($this->config['type'] === 'ldap') {
|
||||
$note = mt(
|
||||
'setup',
|
||||
'Before you are able to authenticate using the LDAP connection defined earlier you need to'
|
||||
. ' provide some more information so that Icinga Web 2 is able to locate account details.'
|
||||
);
|
||||
} else { // if ($this->config['type'] === 'autologin'
|
||||
$note = mt(
|
||||
'setup',
|
||||
'You\'ve chosen to authenticate using a web server\'s mechanism so it may be necessary'
|
||||
. ' to adjust usernames before any permissions, restrictions, etc. are being applied.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array('value' => $note)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
}
|
||||
|
||||
if ($this->config['type'] === 'db') {
|
||||
$backendForm = new DbBackendForm();
|
||||
$backendForm->createElements($formData)->removeElement('resource');
|
||||
} elseif ($this->config['type'] === 'ldap') {
|
||||
$backendForm = new LdapBackendForm();
|
||||
$backendForm->createElements($formData)->removeElement('resource');
|
||||
} else { // $this->config['type'] === 'autologin'
|
||||
$backendForm = new AutologinBackendForm();
|
||||
$backendForm->createElements($formData);
|
||||
}
|
||||
|
||||
$this->addElements($backendForm->getElements());
|
||||
$this->getElement('name')->setValue('icingaweb');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether it's possible to authenticate using the configured backend
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
|
||||
if ($this->config['type'] === 'ldap' && false === LdapBackendForm::isValidAuthenticationBackend($this)) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to this form by which the user can skip the authentication validation
|
||||
*/
|
||||
protected function addSkipValidationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'order' => 2,
|
||||
'ignore' => true,
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Skip Validation'),
|
||||
'description' => mt('setup', 'Check this to not to validate authentication using this backend')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
|
||||
/**
|
||||
* Wizard page to choose an authentication backend
|
||||
*/
|
||||
class AuthenticationPage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_authentication_type');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Authentication', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'setup',
|
||||
'Please choose how you want to authenticate when accessing Icinga Web 2.'
|
||||
. ' Configuring backend specific details follows in a later step.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$backendTypes = array();
|
||||
if (Platform::extensionLoaded('mysql') || Platform::extensionLoaded('pgsql')) {
|
||||
$backendTypes['db'] = t('Database');
|
||||
}
|
||||
if (Platform::extensionLoaded('ldap')) {
|
||||
$backendTypes['ldap'] = 'LDAP';
|
||||
}
|
||||
$backendTypes['autologin'] = t('Autologin');
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Authentication Type'),
|
||||
'description' => mt('setup', 'The type of authentication to use when accessing Icinga Web 2'),
|
||||
'multiOptions' => $backendTypes
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use PDOException;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Module\Setup\Utils\DbTool;
|
||||
|
||||
/**
|
||||
* Wizard page to define a database user that is able to create databases and tables
|
||||
*/
|
||||
class DatabaseCreationPage extends Form
|
||||
{
|
||||
/**
|
||||
* The resource configuration to use
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The required privileges to setup the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseSetupPrivileges;
|
||||
|
||||
/**
|
||||
* The required privileges to operate the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseUsagePrivileges;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_database_creation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource configuration to use
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setResourceConfig(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the required privileges to setup the database
|
||||
*
|
||||
* @param array $privileges The privileges
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDatabaseSetupPrivileges(array $privileges)
|
||||
{
|
||||
$this->databaseSetupPrivileges = $privileges;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the required privileges to operate the database
|
||||
*
|
||||
* @param array $privileges The privileges
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDatabaseUsagePrivileges(array $privileges)
|
||||
{
|
||||
$this->databaseUsagePrivileges = $privileges;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Database Setup', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'setup',
|
||||
'It seems that either the database you defined earlier does not yet exist and cannot be created'
|
||||
. ' using the provided access credentials or the database does not have the required schema to '
|
||||
. 'be operated by Icinga Web 2. Please provide appropriate access credentials to solve this.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$skipValidation = isset($formData['skip_validation']) && $formData['skip_validation'];
|
||||
$this->addElement(
|
||||
'text',
|
||||
'username',
|
||||
array(
|
||||
'required' => false === $skipValidation,
|
||||
'label' => mt('setup', 'Username'),
|
||||
'description' => mt('setup', 'A user which is able to create databases and/or touch the database schema')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'password',
|
||||
'password',
|
||||
array(
|
||||
'label' => mt('setup', 'Password'),
|
||||
'description' => mt('setup', 'The password for the database user defined above')
|
||||
)
|
||||
);
|
||||
|
||||
if ($skipValidation) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
} else {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 0
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether the defined user has sufficient access rights
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($data['skip_validation']) && $data['skip_validation']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$config = $this->config;
|
||||
$config['username'] = $this->getValue('username');
|
||||
$config['password'] = $this->getValue('password');
|
||||
$db = new DbTool($config);
|
||||
|
||||
try {
|
||||
$db->connectToDb(); // Are we able to login on the database?
|
||||
} catch (PDOException $_) {
|
||||
try {
|
||||
$db->connectToHost(); // Are we able to login on the server?
|
||||
} catch (PDOException $e) {
|
||||
// We are NOT able to login on the server..
|
||||
$this->addError($e->getMessage());
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// In case we are connected the credentials filled into this
|
||||
// form need to be granted to create databases, users...
|
||||
if (false === $db->checkPrivileges($this->databaseSetupPrivileges)) {
|
||||
$this->addError(
|
||||
mt('setup', 'The provided credentials cannot be used to create the database and/or the user.')
|
||||
);
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
|
||||
// ...and to grant all required usage privileges to others
|
||||
if (false === $db->isGrantable($this->databaseUsagePrivileges)) {
|
||||
$this->addError(sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The provided credentials cannot be used to grant all required privileges to the login "%s".'
|
||||
),
|
||||
$this->config['username']
|
||||
));
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to the form by which the user can skip the login and privilege validation
|
||||
*/
|
||||
protected function addSkipValidationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'order' => 2,
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Skip Validation'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Check this to not to validate the ability to login and required privileges'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use PDOException;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\Config\Resource\DbResourceForm;
|
||||
use Icinga\Module\Setup\Utils\DbTool;
|
||||
|
||||
/**
|
||||
* Wizard page to define connection details for a database resource
|
||||
*/
|
||||
class DbResourcePage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_db_resource');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'db'
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Database Resource', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'setup',
|
||||
'Now please configure your database resource. Note that the database itself does not need to'
|
||||
. ' exist at this time as it is going to be created once the wizard is about to be finished.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
} else {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$resourceForm = new DbResourceForm();
|
||||
$this->addElements($resourceForm->createElements($formData)->getElements());
|
||||
$this->getElement('name')->setValue('icingaweb_db');
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'prefix',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'icingaweb_'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether it's possible to connect to the database server
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
|
||||
try {
|
||||
$db = new DbTool($this->getValues());
|
||||
$db->checkConnectivity();
|
||||
} catch (PDOException $e) {
|
||||
$this->addError($e->getMessage());
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to the form by which the user can skip the connection validation
|
||||
*/
|
||||
protected function addSkipValidationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Skip Validation'),
|
||||
'description' => mt('setup', 'Check this to not to validate connectivity with the given database server')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\Config\General\LoggingConfigForm;
|
||||
|
||||
/**
|
||||
* Wizard page to define the application and logging configuration
|
||||
*/
|
||||
class GeneralConfigPage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_general_config');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Application Configuration', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'setup',
|
||||
'Now please adjust all application and logging related configuration options to fit your needs.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$loggingForm = new LoggingConfigForm();
|
||||
$this->addElements($loggingForm->createElements($formData)->getElements());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
|
||||
/**
|
||||
* Wizard page to define the connection details for a LDAP resource
|
||||
*/
|
||||
class LdapDiscoveryConfirmPage extends Form
|
||||
{
|
||||
const TYPE_AD = 'MS ActiveDirectory';
|
||||
const TYPE_MISC = 'LDAP';
|
||||
|
||||
private $infoTemplate = <<< 'EOT'
|
||||
<table><tbody>
|
||||
<tr><td><strong>Type:</strong></td><td>{type}</td></tr>
|
||||
<tr><td><strong>Port:</strong></td><td>{port}</td></tr>
|
||||
<tr><td><strong>Root DN:</strong></td><td>{root_dn}</td></tr>
|
||||
<tr><td><strong>User Object Class:</strong></td><td>{user_class}</td></tr>
|
||||
<tr><td><strong>User Name Attribute:</strong></td><td>{user_attribute}</td></tr>
|
||||
</tbody></table>
|
||||
EOT;
|
||||
|
||||
/**
|
||||
* The previous configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_ldap_discovery_confirm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource configuration to use
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setResourceConfig(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource configuration as Config object
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getResourceConfig()
|
||||
{
|
||||
return new Config($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$resource = $this->config['resource'];
|
||||
$backend = $this->config['backend'];
|
||||
$html = $this->infoTemplate;
|
||||
$html = str_replace('{type}', $this->config['type'], $html);
|
||||
$html = str_replace('{hostname}', $resource['hostname'], $html);
|
||||
$html = str_replace('{port}', $resource['port'], $html);
|
||||
$html = str_replace('{root_dn}', $resource['root_dn'], $html);
|
||||
$html = str_replace('{user_attribute}', $backend['user_name_attribute'], $html);
|
||||
$html = str_replace('{user_class}', $backend['user_class'], $html);
|
||||
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'LDAP Discovery Results', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => sprintf(
|
||||
mt('setup', 'The following directory service has been found on domain "%s":'),
|
||||
$this->config['domain']
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'suggestion',
|
||||
array(
|
||||
'value' => $html,
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array(
|
||||
'HtmlTag', array('tag' => 'div')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'confirm',
|
||||
array(
|
||||
'value' => '1',
|
||||
'label' => mt('setup', 'Use this configuration?')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether a BIND-request is successful
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getValues($suppressArrayNotation = false)
|
||||
{
|
||||
if ($this->getValue('confirm') === '1') {
|
||||
// use configuration
|
||||
return $this->config;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\LdapDiscoveryForm;
|
||||
|
||||
/**
|
||||
* Wizard page to define the connection details for a LDAP resource
|
||||
*/
|
||||
class LdapDiscoveryPage extends Form
|
||||
{
|
||||
/**
|
||||
* @var LdapDiscoveryForm
|
||||
*/
|
||||
private $discoveryForm;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_ldap_discovery');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'LDAP Discovery', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'setup',
|
||||
'You can use this page to discover LDAP or ActiveDirectory servers ' .
|
||||
' for authentication. If you don\' want to execute a discovery, just skip this step.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->discoveryForm = new LdapDiscoveryForm();
|
||||
$this->addElements($this->discoveryForm->createElements($formData)->getElements());
|
||||
$this->getElement('domain')->setRequired(
|
||||
isset($formData['skip_validation']) === false || ! $formData['skip_validation']
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Skip'),
|
||||
'description' => mt('setup', 'Do not discover LDAP servers and enter all settings manually.')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether a BIND-request is successful
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $data['skip_validation'] && false === $this->discoveryForm->isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getValues($suppressArrayNotation = false)
|
||||
{
|
||||
if (! isset($this->discoveryForm) || ! $this->discoveryForm->hasSuggestion()) {
|
||||
return null;
|
||||
}
|
||||
return array(
|
||||
'domain' => $this->getValue('domain'),
|
||||
'type' => $this->discoveryForm->isAd() ?
|
||||
LdapDiscoveryConfirmPage::TYPE_AD : LdapDiscoveryConfirmPage::TYPE_MISC,
|
||||
'resource' => $this->discoveryForm->suggestResourceSettings(),
|
||||
'backend' => $this->discoveryForm->suggestBackendSettings()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
use Icinga\Form\Config\Resource\LdapResourceForm;
|
||||
|
||||
/**
|
||||
* Wizard page to define the connection details for a LDAP resource
|
||||
*/
|
||||
class LdapResourcePage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_ldap_resource');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'ldap'
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'LDAP Resource', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt(
|
||||
'setup',
|
||||
'Now please configure your AD/LDAP resource. This will later '
|
||||
. 'be used to authenticate users logging in to Icinga Web 2.'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
} else {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$resourceForm = new LdapResourceForm();
|
||||
$this->addElements($resourceForm->createElements($formData)->getElements());
|
||||
$this->getElement('name')->setValue('icingaweb_ldap');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether a BIND-request is successful
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
|
||||
if (false === LdapResourceForm::isValidResource($this)) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a checkbox to the form by which the user can skip the connection validation
|
||||
*/
|
||||
protected function addSkipValidationCheckbox()
|
||||
{
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'skip_validation',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Skip Validation'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Check this to not to validate connectivity with the given directory service'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Session;
|
||||
use Icinga\Web\Request;
|
||||
|
||||
class ModulePage extends Form
|
||||
{
|
||||
protected $session;
|
||||
|
||||
protected $wizards;
|
||||
|
||||
protected $modules;
|
||||
|
||||
protected $pageData;
|
||||
|
||||
protected $modulePaths;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_modules');
|
||||
$this->setViewScript('form/setup-modules.phtml');
|
||||
$this->session = Session::getSession()->getNamespace(get_class($this));
|
||||
|
||||
$this->modulePaths = array();
|
||||
if (($appModulePath = realpath(Icinga::app()->getApplicationDir() . '/../modules')) !== false) {
|
||||
$this->modulePaths[] = $appModulePath;
|
||||
}
|
||||
}
|
||||
|
||||
public function setPageData(array $pageData)
|
||||
{
|
||||
$this->pageData = $pageData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleRequest(Request $request = null)
|
||||
{
|
||||
if ($this->wasSent($this->getRequestData($request))) {
|
||||
if (($newModule = $request->getPost('module')) !== null) {
|
||||
$this->setCurrentModule($newModule);
|
||||
$this->getResponse()->redirectAndExit($this->getRedirectUrl());
|
||||
} else {
|
||||
// The user submitted this form but with the parent wizard's navigation
|
||||
// buttons so it's now up to the parent wizard to handle the request..
|
||||
}
|
||||
} else {
|
||||
$wizard = $this->getCurrentWizard();
|
||||
$wizardPage = $wizard->getCurrentPage();
|
||||
|
||||
$wizard->handleRequest($request);
|
||||
if ($wizard->isFinished() && $wizardPage->wasSent($wizardPage->getRequestData($request))) {
|
||||
$wizards = $this->getWizards();
|
||||
|
||||
$newModule = null;
|
||||
foreach ($wizards as $moduleName => $moduleWizard) {
|
||||
if (false === $moduleWizard->isFinished()) {
|
||||
$newModule = $moduleName;
|
||||
}
|
||||
}
|
||||
|
||||
if ($newModule === null) {
|
||||
// In case all module wizards were completed just pick the first one again
|
||||
reset($wizards);
|
||||
$newModule = key($wizards);
|
||||
}
|
||||
|
||||
$this->setCurrentModule($newModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function clearSession()
|
||||
{
|
||||
$this->session->clear();
|
||||
foreach ($this->getWizards() as $wizard) {
|
||||
$wizard->clearSession();
|
||||
}
|
||||
}
|
||||
|
||||
public function setCurrentModule($moduleName)
|
||||
{
|
||||
if (false === array_key_exists($moduleName, $this->getWizards())) {
|
||||
throw new InvalidArgumentException(sprintf('Module "%s" does not provide a setup wizard', $moduleName));
|
||||
}
|
||||
|
||||
$this->session->currentModule = $moduleName;
|
||||
}
|
||||
|
||||
public function getCurrentModule()
|
||||
{
|
||||
$moduleName = $this->session->get('currentModule');
|
||||
if ($moduleName === null) {
|
||||
$moduleName = key($this->getWizards());
|
||||
$this->setCurrentModule($moduleName);
|
||||
}
|
||||
|
||||
return $moduleName;
|
||||
}
|
||||
|
||||
public function getCurrentWizard()
|
||||
{
|
||||
$wizards = $this->getWizards();
|
||||
return $wizards[$this->getCurrentModule()];
|
||||
}
|
||||
|
||||
public function getModules()
|
||||
{
|
||||
if ($this->modules !== null) {
|
||||
return $this->modules;
|
||||
} else {
|
||||
$this->modules = array();
|
||||
}
|
||||
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
$moduleManager->detectInstalledModules($this->modulePaths);
|
||||
foreach ($moduleManager->listInstalledModules() as $moduleName) {
|
||||
$this->modules[] = $moduleManager->loadModule($moduleName)->getModule($moduleName);
|
||||
}
|
||||
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
public function getWizards()
|
||||
{
|
||||
if ($this->wizards !== null) {
|
||||
return $this->wizards;
|
||||
} else {
|
||||
$this->wizards = array();
|
||||
}
|
||||
|
||||
foreach ($this->getModules() as $module) {
|
||||
if ($module->providesSetupWizard()) {
|
||||
$this->wizards[$module->getName()] = $module->getSetupWizard();
|
||||
}
|
||||
}
|
||||
|
||||
$this->mergePageData($this->wizards);
|
||||
return $this->wizards;
|
||||
}
|
||||
|
||||
protected function mergePageData(array $wizards)
|
||||
{
|
||||
foreach ($wizards as $wizard) {
|
||||
$wizardPageData = & $wizard->getPageData();
|
||||
foreach ($this->pageData as $pageName => $pageData) {
|
||||
$wizardPageData[$pageName] = $pageData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Web\Form\Element\Note;
|
||||
|
||||
/**
|
||||
* Wizard page to choose a preference backend
|
||||
*/
|
||||
class PreferencesPage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_preferences_type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-select "db" as preference backend and add a hint to the select element
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function showDatabaseNote()
|
||||
{
|
||||
$this->getElement('type')
|
||||
->setValue('db')
|
||||
->setDescription(
|
||||
mt(
|
||||
'setup',
|
||||
'Note that choosing "Database" causes Icinga Web 2 to use the same database as for authentication.'
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'title',
|
||||
array(
|
||||
'value' => mt('setup', 'Preferences', 'setup.page.title'),
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
array('HtmlTag', array('tag' => 'h2'))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
new Note(
|
||||
'description',
|
||||
array(
|
||||
'value' => mt('setup', 'Please choose how Icinga Web 2 should store user preferences.')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$storageTypes = array();
|
||||
$storageTypes['ini'] = t('File System (INI Files)');
|
||||
if (Platform::extensionLoaded('mysql') || Platform::extensionLoaded('pgsql')) {
|
||||
$storageTypes['db'] = t('Database');
|
||||
}
|
||||
$storageTypes['null'] = t('Don\'t Store Preferences');
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => t('User Preference Storage Type'),
|
||||
'multiOptions' => $storageTypes
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Module\Setup\Requirements;
|
||||
|
||||
/**
|
||||
* Wizard page to list setup requirements
|
||||
*/
|
||||
class RequirementsPage extends Form
|
||||
{
|
||||
/**
|
||||
* The requirements to list
|
||||
*
|
||||
* @var Requirements
|
||||
*/
|
||||
protected $requirements;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_requirements');
|
||||
$this->setViewScript('form/setup-requirements.phtml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the requirements to list
|
||||
*
|
||||
* @param Requirements $requirements
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRequirements(Requirements $requirements)
|
||||
{
|
||||
$this->requirements = $requirements;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the requirements to list
|
||||
*
|
||||
* @return Requirements
|
||||
*/
|
||||
public function getRequirements()
|
||||
{
|
||||
return $this->requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether the requirements are fulfilled
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($data)
|
||||
{
|
||||
if (false === parent::isValid($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->requirements->fulfilled();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Wizard page that displays a summary of what is going to be "done"
|
||||
*/
|
||||
class SummaryPage extends Form
|
||||
{
|
||||
/**
|
||||
* The title of what is being set up
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The summary to show
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $summary;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_summary');
|
||||
$this->setViewScript('form/setup-summary.phtml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title of what is being set up
|
||||
*
|
||||
* @param string $title
|
||||
*/
|
||||
public function setSubjectTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title of what is being set up
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubjectTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the summary to show
|
||||
*
|
||||
* @param array $summary
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSummary(array $summary)
|
||||
{
|
||||
$this->summary = $summary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the summary to show
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSummary()
|
||||
{
|
||||
return $this->summary;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Form;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Module\Setup\Web\Form\Validator\TokenValidator;
|
||||
|
||||
/**
|
||||
* Wizard page to authenticate and welcome the user
|
||||
*/
|
||||
class WelcomePage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_welcome');
|
||||
$this->setViewScript('form/setup-welcome.phtml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'token',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => mt('setup', 'Setup Token'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'For security reasons we need to ensure that you are permitted to run this wizard.'
|
||||
. ' Please provide a token by following the instructions below.'
|
||||
),
|
||||
'validators' => array(new TokenValidator(Icinga::app()->getConfigDir() . '/setup.token'))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Wizard;
|
||||
|
||||
$radioElem = $form->getElement('user_type');
|
||||
$showRadioBoxes = strpos(strtolower(get_class($radioElem)), 'radio') !== false;
|
||||
|
||||
?>
|
||||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement('title'); ?>
|
||||
<?= $form->getElement('description'); ?>
|
||||
<?php if (($byNameElem = $form->getElement('by_name')) !== null): ?>
|
||||
<div>
|
||||
<div class="instructions">
|
||||
<?= $byNameElem; ?>
|
||||
</div>
|
||||
<?php if ($showRadioBoxes): ?>
|
||||
<div class="radiobox">
|
||||
<label>
|
||||
<input type="radio" name="user_type" value="by_name"<?= $radioElem->getValue() === 'by_name' ? ' checked' : ''; ?>>
|
||||
<?= $radioElem->getMultiOption('by_name'); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php if (($existingUserElem = $form->getElement('existing_user')) !== null): ?>
|
||||
<div>
|
||||
<div class="instructions">
|
||||
<?= $existingUserElem; ?>
|
||||
</div>
|
||||
<?php if ($showRadioBoxes): ?>
|
||||
<div class="radiobox">
|
||||
<label>
|
||||
<input type="radio" name="user_type" value="existing_user"<?= $radioElem->getValue() === 'existing_user' ? ' checked' : ''; ?>>
|
||||
<?= $radioElem->getMultiOption('existing_user'); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php if (($newUserElem = $form->getElement('new_user')) !== null): ?>
|
||||
<div>
|
||||
<div class="instructions">
|
||||
<?= $newUserElem; ?>
|
||||
<?= $form->getElement('new_user_password'); ?>
|
||||
<?= $form->getElement('new_user_2ndpass'); ?>
|
||||
</div>
|
||||
<?php if ($showRadioBoxes): ?>
|
||||
<div class="radiobox">
|
||||
<label>
|
||||
<input type="radio" name="user_type" value="new_user"<?= $radioElem->getValue() === 'new_user' ? ' checked' : ''; ?>>
|
||||
<?= $radioElem->getMultiOption('new_user'); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php if (false === $showRadioBoxes): ?>
|
||||
<?= $radioElem; ?>
|
||||
<?php endif ?>
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<div class="buttons">
|
||||
<?php
|
||||
$btn = clone $form->getElement(Wizard::BTN_NEXT);
|
||||
$btn->setAttrib('class', 'double');
|
||||
$btn->setAttrib('tabindex', -1);
|
||||
echo $btn;
|
||||
?>
|
||||
<?= $form->getElement(Wizard::BTN_PREV); ?>
|
||||
<?= $form->getElement(Wizard::BTN_NEXT); ?>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Wizard;
|
||||
|
||||
?>
|
||||
<div class="module-menu">
|
||||
<p><?= mt('setup', 'The following modules can be set up by using a web-based wizard as well. To setup a module, just complete its wizard and advance to the summary!'); ?></p>
|
||||
<p><?= mt('setup', 'You can freely switch to a module\'s wizard by clicking its name below. The wizard you are currently looking at is written in bold. A small tick is shown on the right once a wizard has been completed.'); ?></p>
|
||||
<form name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<ul>
|
||||
<?php $allFinished = true; ?>
|
||||
<?php foreach ($form->getModules() as $module): ?>
|
||||
<?php if ($module->providesSetupWizard()): ?>
|
||||
<li>
|
||||
<?php $isActive = $module->getName() === $form->getCurrentModule(); ?>
|
||||
<button type="submit" name="module" value="<?= $module->getName(); ?>">
|
||||
<?= $isActive ? '<strong>' : '' ?><?= $module->getTitle(); ?><?= $isActive ? '</strong>' : '' ?>
|
||||
</button>
|
||||
<?php if ($module->getSetupWizard()->isFinished()): ?>
|
||||
<?= $this->icon('acknowledgement.png', mt('setup', 'Completed', 'setup.modules.wizard.state')); ?>
|
||||
<?php else: ?>
|
||||
<?php $allFinished = false; ?>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</form>
|
||||
<?php if ($allFinished): ?>
|
||||
<p class="all-completed"><?= mt('setup', 'You\'ve completed all module wizards!'); ?></p>
|
||||
<?php else: ?>
|
||||
<p style="font-size: 80%;"><?= mt('setup', 'Note that you can skip a specific module by just not completing its wizard.'); ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="module-wizard">
|
||||
<?= $form->getCurrentWizard()->getForm()->render(); ?>
|
||||
</div>
|
||||
<form name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<div class="buttons">
|
||||
<?= $form->getElement(Wizard::BTN_PREV); ?>
|
||||
<?= $form->getElement(Wizard::BTN_NEXT); ?>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Wizard;
|
||||
use Icinga\Module\Setup\Requirements;
|
||||
|
||||
$requirements = $form->getRequirements();
|
||||
|
||||
?>
|
||||
<table class="requirements">
|
||||
<tbody>
|
||||
<?php foreach ($requirements as $requirement): ?>
|
||||
<tr>
|
||||
<td><h2><?= $requirement->title; ?></h2></td>
|
||||
<td style="width: 50%"><?= $requirement->description; ?></td>
|
||||
<td class="state <?= $requirement->state === Requirements::STATE_OK ? 'fulfilled' : (
|
||||
$requirement->state === Requirements::STATE_OPTIONAL ? 'not-available' : 'missing'
|
||||
); ?>"><?= $requirement->message; ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="btn-update">
|
||||
<div class="buttons">
|
||||
<a href="<?= $this->href(); ?>" class="button-like"><?= mt('setup', 'Update'); ?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<div class="buttons" style="margin: 0 0 1.5em;">
|
||||
<?= $form->getElement(Wizard::BTN_PREV); ?>
|
||||
<?php
|
||||
$btn = $form->getElement(Wizard::BTN_NEXT);
|
||||
if (false === $requirements->fulfilled()) {
|
||||
$btn->setAttrib('disabled', 1);
|
||||
}
|
||||
echo $btn;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Wizard;
|
||||
|
||||
?>
|
||||
<p><?= sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The wizard is now complete. You can review the changes supposed to be made before setting up %1$s.'
|
||||
. ' Make sure that everything is correct (Feel free to navigate back to make any corrections!) so'
|
||||
. ' that you can start using %1$s right after it has successfully been set up.'
|
||||
),
|
||||
$form->getSubjectTitle()
|
||||
); ?></p>
|
||||
<div class="summary">
|
||||
<?php foreach ($form->getSummary() as $pageHtml): ?>
|
||||
<?php if ($pageHtml): ?>
|
||||
<div class="page">
|
||||
<?= $pageHtml; ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<div class="buttons">
|
||||
<?= $form->getElement(Wizard::BTN_PREV); ?>
|
||||
<?= $form->getElement(Wizard::BTN_NEXT)->setAttrib('class', 'finish'); ?>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Web\Wizard;
|
||||
|
||||
$setupTokenPath = rtrim(Icinga::app()->getConfigDir(), '/') . '/setup.token';
|
||||
$cliPath = realpath(Icinga::app()->getApplicationDir() . '/../bin/icingacli');
|
||||
|
||||
?>
|
||||
<div class="welcome-page">
|
||||
<h2><?= mt('setup', 'Welcome to the configuration of Icinga Web 2!') ?></h2>
|
||||
<div class="info">
|
||||
<p><?= sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'Icinga Web 2 is the next generation monitoring web interface,'
|
||||
. ' framework and CLI tool developed by the %s.'
|
||||
),
|
||||
'<a href="https://www.icinga.org/community/team/">' . mt('setup', 'Icinga Project') . '</a>'
|
||||
); ?></p>
|
||||
<p><?= mt(
|
||||
'setup',
|
||||
'Responsive and fast, rewritten from scratch supporting multiple backends and'
|
||||
. ' providing a CLI tool. Compatible with Icinga Core 2.x and 1.x.'
|
||||
); ?></p>
|
||||
<p><?= sprintf(
|
||||
mt('setup', 'Check the Icinga website for some %s.', 'setup.welcome.screenshots'),
|
||||
'<a href="https://www.icinga.org/icinga/screenshots/icinga-web-2/">'
|
||||
. mt('setup', 'insights', 'setup.welcome.screenshots.label') . '</a>'
|
||||
); ?></p>
|
||||
</div>
|
||||
<?php if (false === file_exists($setupTokenPath) && file_exists(Config::resolvePath('config.ini'))): ?>
|
||||
<p class="restart-warning"><?= mt(
|
||||
'setup',
|
||||
'You\'ve already completed the configuration of Icinga Web 2. Note that most of your configuration'
|
||||
. ' files will be overwritten in case you\'ll re-configure Icinga Web 2 using this wizard!'
|
||||
); ?></p>
|
||||
<?php else: ?>
|
||||
<p><?= mt(
|
||||
'setup',
|
||||
'This wizard will guide you through the configuration of Icinga Web 2. Once completed and successfully'
|
||||
. ' finished you are able to log in and to explore all the new and stunning features!'
|
||||
); ?></p>
|
||||
<?php endif ?>
|
||||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement('token'); ?>
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<div class="buttons">
|
||||
<?= $form->getElement(Wizard::BTN_NEXT); ?>
|
||||
</div>
|
||||
</form>
|
||||
<div class="note">
|
||||
<div class="title">
|
||||
<img src="<?= $this->href('img/icons/comment.png'); ?>" alt="<?= mt('setup', 'Note'); ?>">
|
||||
<strong>Generating a New Setup Token</strong>
|
||||
</div>
|
||||
<div>
|
||||
<p><?=
|
||||
mt(
|
||||
'setup',
|
||||
'To run this wizard a user needs to authenticate using a token which is usually'
|
||||
. ' provided to him by an administrator who\'d followed the instructions below.'
|
||||
); ?></p>
|
||||
<p><?= mt('setup', 'If you\'ve got the IcingaCLI installed you can do the following:'); ?></p>
|
||||
<div class="code">
|
||||
<span><?= $cliPath ? $cliPath : 'icingacli'; ?> setup config createDirectory <?= ($user = Platform::getPhpUser()) !== null ? $user : 'your_webserver_group'; ?>;</span>
|
||||
<span><?= $cliPath ? $cliPath : 'icingacli'; ?> setup token create;</span>
|
||||
</div>
|
||||
<p><?= mt('setup', 'In case the IcingaCLI is missing you can create the token manually:'); ?></p>
|
||||
<div class="code">
|
||||
<span>su <?= ($user = Platform::getPhpUser()) !== null ? $user : 'your_webserver_group'; ?> && mkdir -m 2770 <?= dirname($setupTokenPath); ?>;</span>
|
||||
<span>head -c 12 /dev/urandom | base64 | tee <?= $setupTokenPath; ?>;</span>
|
||||
<span>chmod 0660 <?= $setupTokenPath; ?>;</span>
|
||||
</div>
|
||||
<p style="font-size: 85%;"><?= sprintf(
|
||||
mt('setup', 'Please see the %s for an extensive description on how to access and use this wizard.'),
|
||||
'<a href="http://docs.icinga.org/">' . mt('setup', 'Icinga Web 2 documentation') . '</a>' // TODO: Add link to iw2 docs which points to the installation topic
|
||||
); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Notification;
|
||||
|
||||
$pages = $wizard->getPages();
|
||||
$finished = isset($success);
|
||||
$configPages = array_slice($pages, 2, count($pages) - 4, true);
|
||||
$currentPos = array_search($wizard->getCurrentPage(), $pages, true);
|
||||
list($configPagesLeft, $configPagesRight) = array_chunk($configPages, count($configPages) / 2, true);
|
||||
|
||||
$visitedPages = array_keys($wizard->getPageData());
|
||||
$maxProgress = @max(array_keys(array_filter(
|
||||
$pages,
|
||||
function ($page) use ($visitedPages) { return in_array($page->getName(), $visitedPages); }
|
||||
)));
|
||||
|
||||
?>
|
||||
<ul id="notifications"><?php
|
||||
|
||||
$notifications = Notification::getInstance();
|
||||
if ($notifications->hasMessages()) {
|
||||
foreach ($notifications->getMessages() as $m) {
|
||||
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
|
||||
}
|
||||
}
|
||||
|
||||
?></ul>
|
||||
<div id="setup" data-base-target="layout">
|
||||
<div class="header">
|
||||
<?= $this->img('img/logo_icinga_big.png'); ?>
|
||||
<div class="progress-bar">
|
||||
<div class="step" style="width: 10%;">
|
||||
<h1><?= mt('setup', 'Welcome', 'setup.progress'); ?></h1>
|
||||
<?php $stateClass = $finished || $currentPos > 0 ? 'complete' : (
|
||||
$maxProgress > 0 ? 'visited' : 'active'
|
||||
); ?>
|
||||
<table><tbody><tr>
|
||||
<td class="left"></td>
|
||||
<td class="middle"><div class="bubble <?= $stateClass; ?>"></div></td>
|
||||
<td class="right"><div class="line right <?= $stateClass; ?>"></div></td>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
<div class="step" style="width: 10%;">
|
||||
<h1><?= mt('setup', 'Requirements', 'setup.progress'); ?></h1>
|
||||
<?php $stateClass = $finished || $currentPos > 1 ? ' complete' : (
|
||||
$maxProgress > 1 ? ' visited' : (
|
||||
$currentPos === 1 ? ' active' : ''
|
||||
)
|
||||
); ?>
|
||||
<table><tbody><tr>
|
||||
<td class="left"><div class="line left<?= $stateClass; ?>"></div></td>
|
||||
<td class="middle"><div class="bubble<?= $stateClass; ?>"></div></td>
|
||||
<td class="right"><div class="line right<?= $stateClass; ?>"></div></td>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
<div class="step" style="width: 50%;">
|
||||
<h1><?= mt('setup', 'Configuration', 'setup.progress'); ?></h1>
|
||||
<table><tbody><tr>
|
||||
<td class="left">
|
||||
<?php
|
||||
$firstPage = current($configPagesLeft);
|
||||
$lastPage = end($configPagesLeft);
|
||||
$lineWidth = round(100 / count($configPagesLeft), 2, PHP_ROUND_HALF_DOWN);
|
||||
?>
|
||||
<?php foreach ($configPagesLeft as $pos => $page): ?>
|
||||
<?php $stateClass = $finished || $pos < $currentPos ? ' complete' : (
|
||||
$pos < $maxProgress ? ' visited' : ($currentPos > 1 ? ' active' : '')
|
||||
); ?>
|
||||
<?php if ($page === $firstPage): ?>
|
||||
<div class="line left<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-right: 0"></div>
|
||||
<?php elseif ($page === $lastPage): ?>
|
||||
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-right: -0.1em;"></div>
|
||||
<?php else: ?>
|
||||
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%;"></div>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</td>
|
||||
<td class="middle">
|
||||
<div class="bubble<?= array_key_exists($currentPos, $configPagesLeft) ? (
|
||||
key($configPagesRight) <= $maxProgress ? ' visited' : ' active') : (
|
||||
$finished || $currentPos > 1 ? ' complete' : (
|
||||
key($configPagesRight) < $maxProgress ? ' visited' : ''
|
||||
)
|
||||
); ?>"></div>
|
||||
</td>
|
||||
<td class="right">
|
||||
<?php
|
||||
$firstPage = current($configPagesRight);
|
||||
$lastPage = end($configPagesRight);
|
||||
$lineWidth = round(100 / count($configPagesRight), 2, PHP_ROUND_HALF_DOWN);
|
||||
?>
|
||||
<?php foreach ($configPagesRight as $pos => $page): ?>
|
||||
<?php $stateClass = $finished || $pos < $currentPos ? ' complete' : (
|
||||
$pos < $maxProgress ? ' visited' : ($currentPos > 1 ? ' active' : '')
|
||||
); ?>
|
||||
<?php if ($page === $firstPage): ?>
|
||||
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-left: -0.1em;"></div>
|
||||
<?php elseif ($page === $lastPage): ?>
|
||||
<div class="line right<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-left: 0;"></div>
|
||||
<?php else: ?>
|
||||
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%;"></div>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</td>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
<div class="step" style="width: 10%;">
|
||||
<h1><?= mt('setup', 'Modules', 'setup.progress'); ?></h1>
|
||||
<?php $stateClass = $finished || $currentPos > count($pages) - 2 ? ' complete' : (
|
||||
$maxProgress > count($pages) - 2 ? ' visited' : ($currentPos === count($pages) - 2 ? ' active' : '')
|
||||
); ?>
|
||||
<table><tbody><tr>
|
||||
<td class="left"><div class="line left<?= $stateClass; ?>"></div></td>
|
||||
<td class="middle"><div class="bubble<?= $stateClass; ?>"></div></td>
|
||||
<td class="right"><div class="line right<?= $stateClass; ?>"></div></td>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
<div class="step" style="width: 10%;">
|
||||
<h1><?= mt('setup', 'Summary', 'setup.progress'); ?></h1>
|
||||
<?php $stateClass = $finished ? ' complete' : ($currentPos === count($pages) - 1 ? ' active' : ''); ?>
|
||||
<table><tbody><tr>
|
||||
<td class="left"><div class="line left<?= $stateClass; ?>"></div></td>
|
||||
<td class="middle"><div class="bubble<?= $stateClass; ?>"></div></td>
|
||||
<td class="right"><div class="line right<?= $stateClass; ?>"></div></td>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
<div class="step" style="width: 10%;">
|
||||
<h1><?= mt('setup', 'Finish', 'setup.progress'); ?></h1>
|
||||
<?php $stateClass = $finished ? ' active' : ''; ?>
|
||||
<table><tbody><tr>
|
||||
<td class="left"><div class="line left<?= $stateClass; ?>"></div></td>
|
||||
<td class="middle"><div class="bubble<?= $stateClass; ?>"></div></td>
|
||||
<td class="right"></td>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setup-content">
|
||||
<?php if ($finished): ?>
|
||||
<?= $this->render('index/parts/finish.phtml'); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->render('index/parts/wizard.phtml'); ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
<div id="setup-finish">
|
||||
<div class="report">
|
||||
<?php $firstLine = true; ?>
|
||||
<?php foreach ($report as $entry): ?>
|
||||
<?php if ($entry): ?>
|
||||
<?php if (false === $firstLine): ?>
|
||||
<div class="line-separator"></div>
|
||||
<?php endif ?>
|
||||
<?= $entry; ?>
|
||||
<?php $firstLine = false; ?>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<?php if ($success): ?>
|
||||
<p class="success"><?= mt('setup', 'Congratulations! Icinga Web 2 has been successfully set up.'); ?></p>
|
||||
<?php else: ?>
|
||||
<p class="failure"><?= mt('setup', 'Sorry! Failed to set up Icinga Web 2 successfully.'); ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<?php if ($success): ?>
|
||||
<a href="<?= $this->href('authentication/login'); ?>" class="button-like login"><?= mt('setup', 'Login to Icinga Web 2'); ?></a>
|
||||
<?php else: ?>
|
||||
<a href="<?= $this->href(); ?>" class="button-like"><?= mt('setup', 'Back'); ?></a>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
<?= $wizard->getForm()->render(); ?>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Exception;
|
||||
|
||||
/**
|
||||
* Class SetupException
|
||||
*
|
||||
* Used to indicate that a setup should be aborted.
|
||||
*/
|
||||
class SetupException extends IcingaException
|
||||
{
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Container to store and handle requirements
|
||||
*/
|
||||
class Requirements implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Identifier representing the state OK
|
||||
*/
|
||||
const STATE_OK = 2;
|
||||
|
||||
/**
|
||||
* Identifier representing the state OPTIONAL
|
||||
*/
|
||||
const STATE_OPTIONAL = 1;
|
||||
|
||||
/**
|
||||
* Identifier representing the state MANDATORY
|
||||
*/
|
||||
const STATE_MANDATORY = 0;
|
||||
|
||||
/**
|
||||
* The registered requirements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $requirements = array();
|
||||
|
||||
/**
|
||||
* Register a requirement
|
||||
*
|
||||
* @param object $requirement The requirement to add
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add($requirement)
|
||||
{
|
||||
$this->requirements[] = $requirement;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all registered requirements
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator of all registered requirements
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->getAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an optional requirement
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $description
|
||||
* @param bool $state
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addOptional($title, $description, $state, $message)
|
||||
{
|
||||
$this->add((object) array(
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'description' => $description,
|
||||
'state' => (bool) $state ? static::STATE_OK : static::STATE_OPTIONAL
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a mandatory requirement
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $description
|
||||
* @param bool $state
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addMandatory($title, $description, $state, $message)
|
||||
{
|
||||
$this->add((object) array(
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'description' => $description,
|
||||
'state' => (bool) $state ? static::STATE_OK : static::STATE_MANDATORY
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given requirements
|
||||
*
|
||||
* @param Requirements $requirements The requirements to register
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function merge(Requirements $requirements)
|
||||
{
|
||||
foreach ($requirements->getAll() as $requirement) {
|
||||
$this->add($requirement);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make all registered requirements being optional
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function allOptional()
|
||||
{
|
||||
foreach ($this->getAll() as $requirement) {
|
||||
if ($requirement->state === static::STATE_MANDATORY) {
|
||||
$requirement->state = static::STATE_OPTIONAL;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether all mandatory requirements are fulfilled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fulfilled()
|
||||
{
|
||||
foreach ($this->getAll() as $requirement) {
|
||||
if ($requirement->state === static::STATE_MANDATORY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
use Icinga\Module\Setup\Exception\SetupException;
|
||||
|
||||
/**
|
||||
* Container for multiple configuration steps
|
||||
*/
|
||||
class Setup implements IteratorAggregate
|
||||
{
|
||||
protected $steps;
|
||||
|
||||
protected $state;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->steps = array();
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->getSteps());
|
||||
}
|
||||
|
||||
public function addStep(Step $step)
|
||||
{
|
||||
$this->steps[] = $step;
|
||||
}
|
||||
|
||||
public function addSteps(array $steps)
|
||||
{
|
||||
foreach ($steps as $step) {
|
||||
$this->addStep($step);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSteps()
|
||||
{
|
||||
return $this->steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the configuration and return whether it succeeded
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->state = true;
|
||||
|
||||
try {
|
||||
foreach ($this->steps as $step) {
|
||||
$this->state &= $step->apply();
|
||||
}
|
||||
} catch (SetupException $_) {
|
||||
$this->state = false;
|
||||
}
|
||||
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a summary of all actions designated to run
|
||||
*
|
||||
* @return array An array of HTML strings
|
||||
*/
|
||||
public function getSummary()
|
||||
{
|
||||
$summaries = array();
|
||||
foreach ($this->steps as $step) {
|
||||
$summaries[] = $step->getSummary();
|
||||
}
|
||||
|
||||
return $summaries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a report of all actions that were run
|
||||
*
|
||||
* @return array An array of HTML strings
|
||||
*/
|
||||
public function getReport()
|
||||
{
|
||||
$reports = array();
|
||||
foreach ($this->steps as $step) {
|
||||
$reports[] = $step->getReport();
|
||||
}
|
||||
|
||||
return $reports;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
/**
|
||||
* Interface for wizards providing a setup and requirements
|
||||
*/
|
||||
interface SetupWizard
|
||||
{
|
||||
/**
|
||||
* Return the setup for this wizard
|
||||
*
|
||||
* @return Setup
|
||||
*/
|
||||
public function getSetup();
|
||||
|
||||
/**
|
||||
* Return the requirements of this wizard
|
||||
*
|
||||
* @return Requirements
|
||||
*/
|
||||
public function getRequirements();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
/**
|
||||
* Class to implement functionality for a single setup step
|
||||
*/
|
||||
abstract class Step
|
||||
{
|
||||
/**
|
||||
* Apply this step's configuration changes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function apply();
|
||||
|
||||
/**
|
||||
* Return a HTML representation of this step's configuration changes supposed to be made
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getSummary();
|
||||
|
||||
/**
|
||||
* Return a HTML representation of this step's configuration changes that were made
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getReport();
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Authentication\Backend\DbUserBackend;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class AuthenticationStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $dbError;
|
||||
|
||||
protected $authIniError;
|
||||
|
||||
protected $permIniError;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$success = $this->createAuthenticationIni();
|
||||
if (isset($this->data['adminAccountData']['resourceConfig'])) {
|
||||
$success &= $this->createAccount();
|
||||
}
|
||||
|
||||
$success &= $this->defineInitialAdmin();
|
||||
return $success;
|
||||
}
|
||||
|
||||
protected function createAuthenticationIni()
|
||||
{
|
||||
$config = array();
|
||||
$backendConfig = $this->data['backendConfig'];
|
||||
$backendName = $backendConfig['name'];
|
||||
unset($backendConfig['name']);
|
||||
$config[$backendName] = $backendConfig;
|
||||
if (isset($this->data['resourceName'])) {
|
||||
$config[$backendName]['resource'] = $this->data['resourceName'];
|
||||
}
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config($config),
|
||||
'filename' => Config::resolvePath('authentication.ini')
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->authIniError = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->authIniError = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function defineInitialAdmin()
|
||||
{
|
||||
$config = array();
|
||||
$config['admins'] = array(
|
||||
'users' => $this->data['adminAccountData']['username'],
|
||||
'permission' => '*'
|
||||
);
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config($config),
|
||||
'filename' => Config::resolvePath('permissions.ini')
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->permIniError = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->permIniError = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function createAccount()
|
||||
{
|
||||
try {
|
||||
$backend = new DbUserBackend(
|
||||
ResourceFactory::createResource(new Config($this->data['adminAccountData']['resourceConfig']))
|
||||
);
|
||||
|
||||
if (array_search($this->data['adminAccountData']['username'], $backend->listUsers()) === false) {
|
||||
$backend->addUser(
|
||||
$this->data['adminAccountData']['username'],
|
||||
$this->data['adminAccountData']['password']
|
||||
);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->dbError = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->dbError = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$pageTitle = '<h2>' . mt('setup', 'Authentication', 'setup.page.title') . '</h2>';
|
||||
$backendTitle = '<h3>' . mt('setup', 'Authentication Backend', 'setup.page.title') . '</h3>';
|
||||
$adminTitle = '<h3>' . mt('setup', 'Administration', 'setup.page.title') . '</h3>';
|
||||
|
||||
$authType = $this->data['backendConfig']['backend'];
|
||||
$backendDesc = '<p>' . sprintf(
|
||||
mt('setup', 'Users will authenticate using %s.', 'setup.summary.auth'),
|
||||
$authType === 'db' ? mt('setup', 'a database', 'setup.summary.auth.type') : (
|
||||
$authType === 'ldap' ? 'LDAP' : mt('setup', 'webserver authentication', 'setup.summary.auth.type')
|
||||
)
|
||||
) . '</p>';
|
||||
|
||||
$backendHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Backend Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['backendConfig']['name'] . '</td>'
|
||||
. '</tr>'
|
||||
. ($authType === 'ldap' ? (
|
||||
'<tr>'
|
||||
. '<td><strong>' . t('User Object Class') . '</strong></td>'
|
||||
. '<td>' . $this->data['backendConfig']['user_class'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('User Name Attribute') . '</strong></td>'
|
||||
. '<td>' . $this->data['backendConfig']['user_name_attribute'] . '</td>'
|
||||
. '</tr>'
|
||||
) : ($authType === 'autologin' ? (
|
||||
'<tr>'
|
||||
. '<td><strong>' . t('Filter Pattern') . '</strong></td>'
|
||||
. '<td>' . $this->data['backendConfig']['strip_username_regexp'] . '</td>'
|
||||
. '</tr>'
|
||||
) : ''))
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
|
||||
$adminHtml = '<p>' . (isset($this->data['adminAccountData']['resourceConfig']) ? sprintf(
|
||||
mt('setup', 'Administrative rights will initially be granted to a new account called "%s".'),
|
||||
$this->data['adminAccountData']['username']
|
||||
) : sprintf(
|
||||
mt('setup', 'Administrative rights will initially be granted to an existing account called "%s".'),
|
||||
$this->data['adminAccountData']['username']
|
||||
)) . '</p>';
|
||||
|
||||
return $pageTitle . '<div class="topic">' . $backendDesc . $backendTitle . $backendHtml . '</div>'
|
||||
. '<div class="topic">' . $adminTitle . $adminHtml . '</div>';
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
$report = '';
|
||||
if ($this->authIniError === false) {
|
||||
$message = mt('setup', 'Authentication configuration has been successfully written to: %s');
|
||||
$report .= '<p>' . sprintf($message, Config::resolvePath('authentication.ini')) . '</p>';
|
||||
} elseif ($this->authIniError !== null) {
|
||||
$message = mt('setup', 'Authentication configuration could not be written to: %s; An error occured:');
|
||||
$report .= '<p class="error">' . sprintf($message, Config::resolvePath('authentication.ini')) . '</p>'
|
||||
. '<p>' . $this->authIniError->getMessage() . '</p>';
|
||||
}
|
||||
|
||||
if ($this->dbError === false) {
|
||||
$message = mt('setup', 'Account "%s" has been successfully created.');
|
||||
$report .= '<p>' . sprintf($message, $this->data['adminAccountData']['username']) . '</p>';
|
||||
} elseif ($this->dbError !== null) {
|
||||
$message = mt('setup', 'Unable to create account "%s". An error occured:');
|
||||
$report .= '<p class="error">' . sprintf($message, $this->data['adminAccountData']['username']) . '</p>'
|
||||
. '<p>' . $this->dbError->getMessage() . '</p>';
|
||||
}
|
||||
|
||||
if ($this->permIniError === false) {
|
||||
$message = mt('setup', 'Account "%s" has been successfully defined as initial administrator.');
|
||||
$report .= '<p>' . sprintf($message, $this->data['adminAccountData']['username']) . '</p>';
|
||||
} elseif ($this->permIniError !== null) {
|
||||
$message = mt('setup', 'Unable to define account "%s" as initial administrator. An error occured:');
|
||||
$report .= '<p class="error">' . sprintf($message, $this->data['adminAccountData']['username']) . '</p>'
|
||||
. '<p>' . $this->permIniError->getMessage() . '</p>';
|
||||
}
|
||||
|
||||
return $report;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use Exception;
|
||||
use PDOException;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Module\Setup\Utils\DbTool;
|
||||
use Icinga\Module\Setup\Exception\SetupException;
|
||||
|
||||
class DatabaseStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $error;
|
||||
|
||||
protected $messages;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->messages = array();
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$resourceConfig = $this->data['resourceConfig'];
|
||||
if (isset($this->data['adminName'])) {
|
||||
$resourceConfig['username'] = $this->data['adminName'];
|
||||
if (isset($this->data['adminPassword'])) {
|
||||
$resourceConfig['password'] = $this->data['adminPassword'];
|
||||
}
|
||||
}
|
||||
|
||||
$db = new DbTool($resourceConfig);
|
||||
|
||||
try {
|
||||
if ($resourceConfig['db'] === 'mysql') {
|
||||
$this->setupMysqlDatabase($db);
|
||||
} elseif ($resourceConfig['db'] === 'pgsql') {
|
||||
$this->setupPgsqlDatabase($db);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error = $e;
|
||||
throw new SetupException();
|
||||
}
|
||||
|
||||
$this->error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setupMysqlDatabase(DbTool $db)
|
||||
{
|
||||
try {
|
||||
$db->connectToDb();
|
||||
$this->log(
|
||||
mt('setup', 'Successfully connected to existing database "%s"...'),
|
||||
$this->data['resourceConfig']['dbname']
|
||||
);
|
||||
} catch (PDOException $_) {
|
||||
$db->connectToHost();
|
||||
$this->log(mt('setup', 'Creating new database "%s"...'), $this->data['resourceConfig']['dbname']);
|
||||
$db->exec('CREATE DATABASE ' . $db->quoteIdentifier($this->data['resourceConfig']['dbname']));
|
||||
$db->reconnect($this->data['resourceConfig']['dbname']);
|
||||
}
|
||||
|
||||
if (array_search(key($this->data['tables']), $db->listTables()) !== false) {
|
||||
$this->log(mt('setup', 'Database schema already exists...'));
|
||||
} else {
|
||||
$this->log(mt('setup', 'Creating database schema...'));
|
||||
$db->import(Icinga::app()->getApplicationDir() . '/../etc/schema/mysql.schema.sql');
|
||||
}
|
||||
|
||||
if ($db->hasLogin($this->data['resourceConfig']['username'])) {
|
||||
$this->log(mt('setup', 'Login "%s" already exists...'), $this->data['resourceConfig']['username']);
|
||||
} else {
|
||||
$this->log(mt('setup', 'Creating login "%s"...'), $this->data['resourceConfig']['username']);
|
||||
$db->addLogin($this->data['resourceConfig']['username'], $this->data['resourceConfig']['password']);
|
||||
}
|
||||
|
||||
$username = $this->data['resourceConfig']['username'];
|
||||
if ($db->checkPrivileges($this->data['privileges'], $this->data['tables'], $username)) {
|
||||
$this->log(
|
||||
mt('setup', 'Required privileges were already granted to login "%s".'),
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
} else {
|
||||
$this->log(
|
||||
mt('setup', 'Granting required privileges to login "%s"...'),
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
$db->grantPrivileges(
|
||||
$this->data['privileges'],
|
||||
$this->data['tables'],
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function setupPgsqlDatabase(DbTool $db)
|
||||
{
|
||||
try {
|
||||
$db->connectToDb();
|
||||
$this->log(
|
||||
mt('setup', 'Successfully connected to existing database "%s"...'),
|
||||
$this->data['resourceConfig']['dbname']
|
||||
);
|
||||
} catch (PDOException $_) {
|
||||
$db->connectToHost();
|
||||
$this->log(mt('setup', 'Creating new database "%s"...'), $this->data['resourceConfig']['dbname']);
|
||||
$db->exec(sprintf(
|
||||
"CREATE DATABASE %s WITH ENCODING 'UTF-8'",
|
||||
$db->quoteIdentifier($this->data['resourceConfig']['dbname'])
|
||||
));
|
||||
$db->reconnect($this->data['resourceConfig']['dbname']);
|
||||
}
|
||||
|
||||
if (array_search(key($this->data['tables']), $db->listTables()) !== false) {
|
||||
$this->log(mt('setup', 'Database schema already exists...'));
|
||||
} else {
|
||||
$this->log(mt('setup', 'Creating database schema...'));
|
||||
$db->import(Icinga::app()->getApplicationDir() . '/../etc/schema/pgsql.schema.sql');
|
||||
}
|
||||
|
||||
if ($db->hasLogin($this->data['resourceConfig']['username'])) {
|
||||
$this->log(mt('setup', 'Login "%s" already exists...'), $this->data['resourceConfig']['username']);
|
||||
} else {
|
||||
$this->log(mt('setup', 'Creating login "%s"...'), $this->data['resourceConfig']['username']);
|
||||
$db->addLogin($this->data['resourceConfig']['username'], $this->data['resourceConfig']['password']);
|
||||
}
|
||||
|
||||
$username = $this->data['resourceConfig']['username'];
|
||||
if ($db->checkPrivileges($this->data['privileges'], $this->data['tables'], $username)) {
|
||||
$this->log(
|
||||
mt('setup', 'Required privileges were already granted to login "%s".'),
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
} else {
|
||||
$this->log(
|
||||
mt('setup', 'Granting required privileges to login "%s"...'),
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
$db->grantPrivileges(
|
||||
$this->data['privileges'],
|
||||
$this->data['tables'],
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$resourceConfig = $this->data['resourceConfig'];
|
||||
if (isset($this->data['adminName'])) {
|
||||
$resourceConfig['username'] = $this->data['adminName'];
|
||||
if (isset($this->data['adminPassword'])) {
|
||||
$resourceConfig['password'] = $this->data['adminPassword'];
|
||||
}
|
||||
}
|
||||
|
||||
$db = new DbTool($resourceConfig);
|
||||
|
||||
try {
|
||||
$db->connectToDb();
|
||||
if (array_search(key($this->data['tables']), $db->listTables()) === false) {
|
||||
if ($resourceConfig['username'] !== $this->data['resourceConfig']['username']) {
|
||||
$message = sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The database user "%s" will be used to setup the missing schema required by Icinga'
|
||||
. ' Web 2 in database "%s" and to grant access to it to a new login called "%s".'
|
||||
),
|
||||
$resourceConfig['username'],
|
||||
$resourceConfig['dbname'],
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The database user "%s" will be used to setup the missing'
|
||||
. ' schema required by Icinga Web 2 in database "%s".'
|
||||
),
|
||||
$resourceConfig['username'],
|
||||
$resourceConfig['dbname']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$message = sprintf(
|
||||
mt('setup', 'The database "%s" already seems to be fully set up. No action required.'),
|
||||
$resourceConfig['dbname']
|
||||
);
|
||||
}
|
||||
} catch (PDOException $_) {
|
||||
try {
|
||||
$db->connectToHost();
|
||||
if ($resourceConfig['username'] !== $this->data['resourceConfig']['username']) {
|
||||
if ($db->hasLogin($this->data['resourceConfig']['username'])) {
|
||||
$message = sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The database user "%s" will be used to create the missing database'
|
||||
. ' "%s" with the schema required by Icinga Web 2 and to grant'
|
||||
. ' access to it to an existing login called "%s".'
|
||||
),
|
||||
$resourceConfig['username'],
|
||||
$resourceConfig['dbname'],
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
} else {
|
||||
$message = sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The database user "%s" will be used to create the missing database'
|
||||
. ' "%s" with the schema required by Icinga Web 2 and to grant'
|
||||
. ' access to it to a new login called "%s".'
|
||||
),
|
||||
$resourceConfig['username'],
|
||||
$resourceConfig['dbname'],
|
||||
$this->data['resourceConfig']['username']
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$message = sprintf(
|
||||
mt(
|
||||
'setup',
|
||||
'The database user "%s" will be used to create the missing'
|
||||
. ' database "%s" with the schema required by Icinga Web 2.'
|
||||
),
|
||||
$resourceConfig['username'],
|
||||
$resourceConfig['dbname']
|
||||
);
|
||||
}
|
||||
} catch (Exception $_) {
|
||||
$message = mt(
|
||||
'setup',
|
||||
'No connection to database host possible. You\'ll need to setup the'
|
||||
. ' database with the schema required by Icinga Web 2 manually.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return '<h2>' . mt('setup', 'Database Setup', 'setup.page.title') . '</h2><p>' . $message . '</p>';
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
if ($this->error === false) {
|
||||
return '<p>' . join('</p><p>', $this->messages) . '</p>'
|
||||
. '<p>' . mt('setup', 'The database has been fully set up!') . '</p>';
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt('setup', 'Failed to fully setup the database. An error occured:');
|
||||
return '<p>' . join('</p><p>', $this->messages) . '</p>'
|
||||
. '<p class="error">' . $message . '</p><p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
protected function log()
|
||||
{
|
||||
$this->messages[] = call_user_func_array('sprintf', func_get_args());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class GeneralConfigStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $error;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$config = array();
|
||||
foreach ($this->data['generalConfig'] as $sectionAndPropertyName => $value) {
|
||||
list($section, $property) = explode('_', $sectionAndPropertyName);
|
||||
$config[$section][$property] = $value;
|
||||
}
|
||||
|
||||
$config['preferences']['type'] = $this->data['preferencesType'];
|
||||
if (isset($this->data['preferencesResource'])) {
|
||||
$config['preferences']['resource'] = $this->data['preferencesResource'];
|
||||
}
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config($config),
|
||||
'filename' => Config::resolvePath('config.ini')
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->error = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
$pageTitle = '<h2>' . mt('setup', 'Application Configuration', 'setup.page.title') . '</h2>';
|
||||
$generalTitle = '<h3>' . t('General', 'app.config') . '</h3>';
|
||||
$loggingTitle = '<h3>' . t('Logging', 'app.config') . '</h3>';
|
||||
|
||||
$generalHtml = ''
|
||||
. '<ul>'
|
||||
. '<li>' . sprintf(
|
||||
$this->data['preferencesType'] === 'ini' ? sprintf(
|
||||
t('Preferences will be stored per user account in INI files at: %s'),
|
||||
Config::resolvePath('preferences')
|
||||
) : (
|
||||
$this->data['preferencesType'] === 'db' ? t('Preferences will be stored using a database.') : (
|
||||
t('Preferences will not be persisted across browser sessions.')
|
||||
)
|
||||
)
|
||||
) . '</li>'
|
||||
. '</ul>';
|
||||
|
||||
$type = $this->data['generalConfig']['logging_log'];
|
||||
if ($type === 'none') {
|
||||
$loggingHtml = '<p>' . mt('setup', 'Logging will be disabled.') . '</p>';
|
||||
} else {
|
||||
$level = $this->data['generalConfig']['logging_level'];
|
||||
$loggingHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Type', 'app.config.logging') . '</strong></td>'
|
||||
. '<td>' . ($type === 'syslog' ? 'Syslog' : t('File', 'app.config.logging.type')) . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Level', 'app.config.logging') . '</strong></td>'
|
||||
. '<td>' . ($level === Logger::$levels[Logger::ERROR] ? t('Error', 'app.config.logging.level') : (
|
||||
$level === Logger::$levels[Logger::WARNING] ? t('Warning', 'app.config.logging.level') : (
|
||||
$level === Logger::$levels[Logger::INFO] ? t('Information', 'app.config.logging.level') : (
|
||||
t('Debug', 'app.config.logging.level')
|
||||
)
|
||||
)
|
||||
)) . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. ($type === 'syslog' ? (
|
||||
'<td><strong>' . t('Application Prefix') . '</strong></td>'
|
||||
. '<td>' . $this->data['generalConfig']['logging_application'] . '</td>'
|
||||
) : (
|
||||
'<td><strong>' . t('Filepath') . '</strong></td>'
|
||||
. '<td>' . $this->data['generalConfig']['logging_file'] . '</td>'
|
||||
))
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
}
|
||||
|
||||
return $pageTitle . '<div class="topic">' . $generalTitle . $generalHtml . '</div>'
|
||||
. '<div class="topic">' . $loggingTitle . $loggingHtml . '</div>';
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
if ($this->error === false) {
|
||||
$message = mt('setup', 'General configuration has been successfully written to: %s');
|
||||
return '<p>' . sprintf($message, Config::resolvePath('config.ini')) . '</p>';
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt('setup', 'General configuration could not be written to: %s; An error occured:');
|
||||
return '<p class="error">' . sprintf($message, Config::resolvePath('config.ini')) . '</p>'
|
||||
. '<p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class ResourceStep extends Step
|
||||
{
|
||||
protected $data;
|
||||
|
||||
protected $error;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$resourceConfig = array();
|
||||
if (isset($this->data['dbResourceConfig'])) {
|
||||
$dbConfig = $this->data['dbResourceConfig'];
|
||||
$resourceName = $dbConfig['name'];
|
||||
unset($dbConfig['name']);
|
||||
$resourceConfig[$resourceName] = $dbConfig;
|
||||
}
|
||||
|
||||
if (isset($this->data['ldapResourceConfig'])) {
|
||||
$ldapConfig = $this->data['ldapResourceConfig'];
|
||||
$resourceName = $ldapConfig['name'];
|
||||
unset($ldapConfig['name']);
|
||||
$resourceConfig[$resourceName] = $ldapConfig;
|
||||
}
|
||||
|
||||
try {
|
||||
$writer = new IniWriter(array(
|
||||
'config' => new Config($resourceConfig),
|
||||
'filename' => Config::resolvePath('resources.ini'),
|
||||
'filemode' => 0660
|
||||
));
|
||||
$writer->write();
|
||||
} catch (Exception $e) {
|
||||
$this->error = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
if (isset($this->data['dbResourceConfig']) && isset($this->data['ldapResourceConfig'])) {
|
||||
$pageTitle = '<h2>' . mt('setup', 'Resources', 'setup.page.title') . '</h2>';
|
||||
} else {
|
||||
$pageTitle = '<h2>' . mt('setup', 'Resource', 'setup.page.title') . '</h2>';
|
||||
}
|
||||
|
||||
if (isset($this->data['dbResourceConfig'])) {
|
||||
$dbTitle = '<h3>' . mt('setup', 'Database', 'setup.page.title') . '</h3>';
|
||||
$dbHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Resource Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['dbResourceConfig']['name'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Database Type') . '</strong></td>'
|
||||
. '<td>' . $this->data['dbResourceConfig']['db'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Host') . '</strong></td>'
|
||||
. '<td>' . $this->data['dbResourceConfig']['host'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Port') . '</strong></td>'
|
||||
. '<td>' . $this->data['dbResourceConfig']['port'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Database Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['dbResourceConfig']['dbname'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Username') . '</strong></td>'
|
||||
. '<td>' . $this->data['dbResourceConfig']['username'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Password') . '</strong></td>'
|
||||
. '<td>' . str_repeat('*', strlen($this->data['dbResourceConfig']['password'])) . '</td>'
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
}
|
||||
|
||||
if (isset($this->data['ldapResourceConfig'])) {
|
||||
$ldapTitle = '<h3>LDAP</h3>';
|
||||
$ldapHtml = ''
|
||||
. '<table>'
|
||||
. '<tbody>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Resource Name') . '</strong></td>'
|
||||
. '<td>' . $this->data['ldapResourceConfig']['name'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Host') . '</strong></td>'
|
||||
. '<td>' . $this->data['ldapResourceConfig']['hostname'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Port') . '</strong></td>'
|
||||
. '<td>' . $this->data['ldapResourceConfig']['port'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Root DN') . '</strong></td>'
|
||||
. '<td>' . $this->data['ldapResourceConfig']['root_dn'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Bind DN') . '</strong></td>'
|
||||
. '<td>' . $this->data['ldapResourceConfig']['bind_dn'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('Bind Password') . '</strong></td>'
|
||||
. '<td>' . str_repeat('*', strlen($this->data['ldapResourceConfig']['bind_pw'])) . '</td>'
|
||||
. '</tr>'
|
||||
. '</tbody>'
|
||||
. '</table>';
|
||||
}
|
||||
|
||||
return $pageTitle . (isset($dbTitle) ? '<div class="topic">' . $dbTitle . $dbHtml . '</div>' : '')
|
||||
. (isset($ldapTitle) ? '<div class="topic">' . $ldapTitle . $ldapHtml . '</div>' : '');
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
if ($this->error === false) {
|
||||
$message = mt('setup', 'Resource configuration has been successfully written to: %s');
|
||||
return '<p>' . sprintf($message, Config::resolvePath('resources.ini')) . '</p>';
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt('setup', 'Resource configuration could not be written to: %s; An error occured:');
|
||||
return '<p class="error">' . sprintf($message, Config::resolvePath('resources.ini')) . '</p>'
|
||||
. '<p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,781 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Utils;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use LogicException;
|
||||
use Zend_Db_Adapter_Pdo_Mysql;
|
||||
use Zend_Db_Adapter_Pdo_Pgsql;
|
||||
use Icinga\Util\File;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
/**
|
||||
* Utility class to ease working with databases when setting up Icinga Web 2 or one of its modules
|
||||
*/
|
||||
class DbTool
|
||||
{
|
||||
/**
|
||||
* The PDO database connection
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdoConn;
|
||||
|
||||
/**
|
||||
* The Zend database adapter
|
||||
*
|
||||
* @var Zend_Db_Adapter_Pdo_Abstract
|
||||
*/
|
||||
protected $zendConn;
|
||||
|
||||
/**
|
||||
* The resource configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Whether we are connected to the database from the resource configuration
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $dbFromConfig = false;
|
||||
|
||||
/**
|
||||
* GRANT privilege level identifiers
|
||||
*/
|
||||
const GLOBAL_LEVEL = 1;
|
||||
const PROCEDURE_LEVEL = 2;
|
||||
const DATABASE_LEVEL = 4;
|
||||
const TABLE_LEVEL = 8;
|
||||
const COLUMN_LEVEL = 16;
|
||||
const FUNCTION_LEVEL = 32;
|
||||
|
||||
/**
|
||||
* All MySQL GRANT privileges with their respective level identifiers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mysqlGrantContexts = array(
|
||||
'ALL' => 31,
|
||||
'ALL PRIVILEGES' => 31,
|
||||
'ALTER' => 13,
|
||||
'ALTER ROUTINE' => 7,
|
||||
'CREATE' => 13,
|
||||
'CREATE ROUTINE' => 5,
|
||||
'CREATE TEMPORARY TABLES' => 5,
|
||||
'CREATE USER' => 1,
|
||||
'CREATE VIEW' => 13,
|
||||
'DELETE' => 13,
|
||||
'DROP' => 13,
|
||||
'EXECUTE' => 5, // MySQL reference states this also supports database level, 5.1.73 not though
|
||||
'FILE' => 1,
|
||||
'GRANT OPTION' => 15,
|
||||
'INDEX' => 13,
|
||||
'INSERT' => 29,
|
||||
'LOCK TABLES' => 5,
|
||||
'PROCESS' => 1,
|
||||
'REFERENCES' => 0,
|
||||
'RELOAD' => 1,
|
||||
'REPLICATION CLIENT' => 1,
|
||||
'REPLICATION SLAVE' => 1,
|
||||
'SELECT' => 29,
|
||||
'SHOW DATABASES' => 1,
|
||||
'SHOW VIEW' => 13,
|
||||
'SHUTDOWN' => 1,
|
||||
'SUPER' => 1,
|
||||
'UPDATE' => 29
|
||||
);
|
||||
|
||||
/**
|
||||
* All PostgreSQL GRANT privileges with their respective level identifiers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pgsqlGrantContexts = array(
|
||||
'ALL' => 63,
|
||||
'ALL PRIVILEGES' => 63,
|
||||
'SELECT' => 24,
|
||||
'INSERT' => 24,
|
||||
'UPDATE' => 24,
|
||||
'DELETE' => 8,
|
||||
'TRUNCATE' => 8,
|
||||
'REFERENCES' => 24,
|
||||
'TRIGGER' => 8,
|
||||
'CREATE' => 12,
|
||||
'CONNECT' => 4,
|
||||
'TEMPORARY' => 4,
|
||||
'TEMP' => 4,
|
||||
'EXECUTE' => 32,
|
||||
'USAGE' => 33,
|
||||
'CREATEROLE' => 1
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new DbTool
|
||||
*
|
||||
* @param array $config The resource configuration to use
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the server
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function connectToHost()
|
||||
{
|
||||
$this->assertHostAccess();
|
||||
|
||||
if ($this->config['db'] == 'pgsql') {
|
||||
// PostgreSQL requires us to specify a database on each connection and will use
|
||||
// the current user name as default database in cases none is provided. If
|
||||
// that database doesn't exist (which might be the case here) it will error.
|
||||
// Therefore, we specify the maintenance database 'postgres' as database, which
|
||||
// is most probably present and public. (http://stackoverflow.com/q/4483139)
|
||||
$this->connect('postgres');
|
||||
} else {
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the database
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function connectToDb()
|
||||
{
|
||||
$this->assertHostAccess();
|
||||
$this->assertDatabaseAccess();
|
||||
$this->connect($this->config['dbname']);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that all configuration values exist that are required to connect to a server
|
||||
*
|
||||
* @throws ConfigurationError
|
||||
*/
|
||||
protected function assertHostAccess()
|
||||
{
|
||||
if (false === isset($this->config['db'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server of unknown type');
|
||||
} elseif (false === isset($this->config['host'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a hostname or address');
|
||||
} elseif (false === isset($this->config['port'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a port');
|
||||
} elseif (false === isset($this->config['username'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a username');
|
||||
} elseif (false === isset($this->config['password'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a password');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that all configuration values exist that are required to connect to a database
|
||||
*
|
||||
* @throws ConfigurationError
|
||||
*/
|
||||
protected function assertDatabaseAccess()
|
||||
{
|
||||
if (false === isset($this->config['dbname'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database without a valid database name');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a connection with a database has been established
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
protected function assertConnectedToDb()
|
||||
{
|
||||
if ($this->zendConn === null) {
|
||||
throw new LogicException('Not connected to database');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a connection with the database or just the server by omitting the database name
|
||||
*
|
||||
* @param string $dbname The name of the database to connect to
|
||||
*/
|
||||
public function connect($dbname = null)
|
||||
{
|
||||
$this->_pdoConnect($dbname);
|
||||
if ($dbname !== null) {
|
||||
$this->_zendConnect($dbname);
|
||||
$this->dbFromConfig = $dbname === $this->config['dbname'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reestablish a connection with the database or just the server by omitting the database name
|
||||
*
|
||||
* @param string $dbname The name of the database to connect to
|
||||
*/
|
||||
public function reconnect($dbname = null)
|
||||
{
|
||||
$this->pdoConn = null;
|
||||
$this->zendConn = null;
|
||||
$this->connect($dbname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Zend database adapter
|
||||
*
|
||||
* @param string $dbname The name of the database to connect with
|
||||
*
|
||||
* @throws ConfigurationError In case the resource type is not a supported PDO driver name
|
||||
*/
|
||||
protected function _zendConnect($dbname)
|
||||
{
|
||||
if ($this->zendConn !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$config = array(
|
||||
'dbname' => $dbname,
|
||||
'username' => $this->config['username'],
|
||||
'password' => $this->config['password']
|
||||
);
|
||||
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
$this->zendConn = new Zend_Db_Adapter_Pdo_Mysql($config);
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
$this->zendConn = new Zend_Db_Adapter_Pdo_Pgsql($config);
|
||||
} else {
|
||||
throw new ConfigurationError(
|
||||
'Failed to connect to database. Unsupported PDO driver "%s"',
|
||||
$this->config['db']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize PDO connection
|
||||
*
|
||||
* @param string $dbname The name of the database to connect with
|
||||
*/
|
||||
protected function _pdoConnect($dbname)
|
||||
{
|
||||
if ($this->pdoConn !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pdoConn = new PDO(
|
||||
$this->buildDsn($this->config['db'], $dbname),
|
||||
$this->config['username'],
|
||||
$this->config['password'],
|
||||
array(PDO::ATTR_TIMEOUT => 1, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a datasource name for the given database type and name
|
||||
*
|
||||
* @param string $dbtype
|
||||
* @param string $dbname
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws ConfigurationError In case the passed database type is not supported
|
||||
*/
|
||||
protected function buildDsn($dbtype, $dbname = null)
|
||||
{
|
||||
if ($dbtype === 'mysql') {
|
||||
return 'mysql:host=' . $this->config['host'] . ';port=' . $this->config['port']
|
||||
. ($dbname !== null ? ';dbname=' . $dbname : '');
|
||||
} elseif ($dbtype === 'pgsql') {
|
||||
return 'pgsql:host=' . $this->config['host'] . ';port=' . $this->config['port']
|
||||
. ($dbname !== null ? ';dbname=' . $dbname : '');
|
||||
} else {
|
||||
throw new ConfigurationError(
|
||||
'Failed to build data source name. Unsupported PDO driver "%s"',
|
||||
$dbtype
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to connect to the server and throw an exception if this fails
|
||||
*
|
||||
* @throws PDOException In case an error occurs that does not indicate that authentication failed
|
||||
*/
|
||||
public function checkConnectivity()
|
||||
{
|
||||
try {
|
||||
$this->connectToHost();
|
||||
} catch (PDOException $e) {
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
$code = $e->getCode();
|
||||
if ($code !== 1040 && $code !== 1045) {
|
||||
throw $e;
|
||||
}
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
if (strpos($e->getMessage(), $this->config['username']) === false) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given identifier escaped with backticks
|
||||
*
|
||||
* @param string $identifier The identifier to escape
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws LogicException In case there is no behaviour implemented for the current PDO driver
|
||||
*/
|
||||
public function quoteIdentifier($identifier)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
return '`' . str_replace('`', '``', $identifier) . '`';
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
return '"' . str_replace('"', '""', $identifier) . '"';
|
||||
} else {
|
||||
throw new LogicException('Unable to quote identifier.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given value escaped as string
|
||||
*
|
||||
* @param mixed $value The value to escape
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws LogicException In case there is no behaviour implemented for the current PDO driver
|
||||
*/
|
||||
public function quote($value)
|
||||
{
|
||||
$quoted = $this->pdoConn->quote($value);
|
||||
if ($quoted === false) {
|
||||
throw new LogicException(sprintf('Unable to quote value: %s', $value));
|
||||
}
|
||||
|
||||
return $quoted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a SQL statement and return the affected row count
|
||||
*
|
||||
* Use $params to use a prepared statement.
|
||||
*
|
||||
* @param string $statement The statement to execute
|
||||
* @param array $params The params to bind
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function exec($statement, $params = array())
|
||||
{
|
||||
if (empty($params)) {
|
||||
return $this->pdoConn->exec($statement);
|
||||
}
|
||||
|
||||
$stmt = $this->pdoConn->prepare($statement);
|
||||
$stmt->execute($params);
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a SQL statement and return the result
|
||||
*
|
||||
* Use $params to use a prepared statement.
|
||||
*
|
||||
* @param string $statement The statement to execute
|
||||
* @param array $params The params to bind
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function query($statement, $params = array())
|
||||
{
|
||||
if ($this->zendConn !== null) {
|
||||
return $this->zendConn->query($statement, $params);
|
||||
}
|
||||
|
||||
if (empty($params)) {
|
||||
return $this->pdoConn->query($statement);
|
||||
}
|
||||
|
||||
$stmt = $this->pdoConn->prepare($statement);
|
||||
$stmt->execute($params);
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the given SQL file
|
||||
*
|
||||
* @param string $filepath The file to import
|
||||
*/
|
||||
public function import($filepath)
|
||||
{
|
||||
$file = new File($filepath);
|
||||
$content = join(PHP_EOL, iterator_to_array($file)); // There is no fread() before PHP 5.5 :(
|
||||
|
||||
foreach (preg_split('@;(?! \\\\)@', $content) as $statement) {
|
||||
if (($statement = trim($statement)) !== '') {
|
||||
$this->exec($statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given privileges were granted
|
||||
*
|
||||
* @param array $privileges An array of strings with the required privilege names
|
||||
* @param array $context An array describing the context for which the given privileges need to apply.
|
||||
* Only one or more table names are currently supported
|
||||
* @param string $username The login name for which to check the privileges,
|
||||
* if NULL the current login is used
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkPrivileges(array $privileges, array $context = null, $username = null)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
return $this->checkMysqlPrivileges($privileges, false, $context, $username);
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
return $this->checkPgsqlPrivileges($privileges, false, $context, $username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given privileges are grantable to other users
|
||||
*
|
||||
* @param array $privileges The privileges that should be grantable
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isGrantable($privileges)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
return $this->checkMysqlPrivileges($privileges, true);
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
return $this->checkPgsqlPrivileges($privileges, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant all given privileges to the given user
|
||||
*
|
||||
* @param array $privileges The privilege names to grant
|
||||
* @param array $context An array describing the context for which the given privileges need to apply.
|
||||
* Only one or more table names are currently supported
|
||||
* @param string $username The username to grant the privileges to
|
||||
*/
|
||||
public function grantPrivileges(array $privileges, array $context, $username)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
list($_, $host) = explode('@', $this->query('select current_user()')->fetchColumn());
|
||||
$queryString = sprintf(
|
||||
'GRANT %%s ON %s.%%s TO %s@%s',
|
||||
$this->quoteIdentifier($this->config['dbname']),
|
||||
$this->quoteIdentifier($username),
|
||||
$this->quoteIdentifier($host)
|
||||
);
|
||||
|
||||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach (array_intersect($privileges, array_keys($this->mysqlGrantContexts)) as $privilege) {
|
||||
if (false === empty($context) && $this->mysqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
} elseif ($this->mysqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
$dbPrivileges[] = $privilege;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($tablePrivileges)) {
|
||||
foreach ($context as $table) {
|
||||
$this->exec(
|
||||
sprintf($queryString, join(',', $tablePrivileges), $this->quoteIdentifier($table))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($dbPrivileges)) {
|
||||
$this->exec(sprintf($queryString, join(',', $dbPrivileges), '*'));
|
||||
}
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach (array_intersect($privileges, array_keys($this->pgsqlGrantContexts)) as $privilege) {
|
||||
if (false === empty($context) && $this->pgsqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
} elseif ($this->pgsqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
$dbPrivileges[] = $privilege;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($dbPrivileges)) {
|
||||
$this->exec(sprintf(
|
||||
'GRANT %s ON DATABASE %s TO %s',
|
||||
join(',', $dbPrivileges),
|
||||
$this->config['dbname'],
|
||||
$username
|
||||
));
|
||||
}
|
||||
|
||||
if (false === empty($tablePrivileges)) {
|
||||
foreach ($context as $table) {
|
||||
$this->exec(sprintf(
|
||||
'GRANT %s ON TABLE %s TO %s',
|
||||
join(',', $tablePrivileges),
|
||||
$table,
|
||||
$username
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all existing database tables
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listTables()
|
||||
{
|
||||
$this->assertConnectedToDb();
|
||||
return $this->zendConn->listTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given database login exists
|
||||
*
|
||||
* @param string $username The username to search
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLogin($username)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
$queryString = <<<EOD
|
||||
SELECT 1
|
||||
FROM information_schema.user_privileges
|
||||
WHERE grantee = REPLACE(CONCAT("'", REPLACE(CURRENT_USER(), '@', "'@'"), "'"), :current, :wanted)
|
||||
EOD;
|
||||
|
||||
$query = $this->query(
|
||||
$queryString,
|
||||
array(
|
||||
':current' => $this->config['username'],
|
||||
':wanted' => $username
|
||||
)
|
||||
);
|
||||
return count($query->fetchAll()) > 0;
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
$query = $this->query(
|
||||
'SELECT 1 FROM pg_catalog.pg_user WHERE usename = :ident LIMIT 1',
|
||||
array(':ident' => $username)
|
||||
);
|
||||
return count($query->fetchAll()) === 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new database login
|
||||
*
|
||||
* @param string $username The username of the new login
|
||||
* @param string $password The password of the new login
|
||||
*/
|
||||
public function addLogin($username, $password)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
list($_, $host) = explode('@', $this->query('select current_user()')->fetchColumn());
|
||||
$this->exec(
|
||||
'CREATE USER :user@:host IDENTIFIED BY :passw',
|
||||
array(':user' => $username, ':host' => $host, ':passw' => $password)
|
||||
);
|
||||
} elseif ($this->config['db'] === 'pgsql') {
|
||||
$this->exec(sprintf(
|
||||
'CREATE USER %s WITH PASSWORD %s',
|
||||
$this->quoteIdentifier($username),
|
||||
$this->quote($password)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current user has the given privileges
|
||||
*
|
||||
* @param array $privileges The privilege names
|
||||
* @param bool $requireGrants Only return true when all privileges can be granted to others
|
||||
* @param array $context An array describing the context for which the given privileges need to apply.
|
||||
* Only one or more table names are currently supported
|
||||
* @param string $username The login name to which the passed privileges need to be granted
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkMysqlPrivileges(
|
||||
array $privileges,
|
||||
$requireGrants = false,
|
||||
array $context = null,
|
||||
$username = null
|
||||
) {
|
||||
$mysqlPrivileges = array_intersect($privileges, array_keys($this->mysqlGrantContexts));
|
||||
list($_, $host) = explode('@', $this->query('select current_user()')->fetchColumn());
|
||||
$grantee = "'" . ($username === null ? $this->config['username'] : $username) . "'@'" . $host . "'";
|
||||
$privilegeCondition = sprintf(
|
||||
'privilege_type IN (%s)',
|
||||
join(',', array_map(array($this, 'quote'), $mysqlPrivileges))
|
||||
);
|
||||
|
||||
if (isset($this->config['dbname'])) {
|
||||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach ($mysqlPrivileges as $privilege) {
|
||||
if (false === empty($context) && $this->mysqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
} elseif ($this->mysqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
$dbPrivileges[] = $privilege;
|
||||
}
|
||||
}
|
||||
|
||||
$dbPrivilegesGranted = true;
|
||||
if (false === empty($dbPrivileges)) {
|
||||
$query = $this->query(
|
||||
'SELECT COUNT(*) as matches'
|
||||
. ' FROM information_schema.schema_privileges'
|
||||
. ' WHERE grantee = :grantee'
|
||||
. ' AND table_schema = :dbname'
|
||||
. ' AND ' . $privilegeCondition
|
||||
. ($requireGrants ? " AND is_grantable = 'YES'" : ''),
|
||||
array(':grantee' => $grantee, ':dbname' => $this->config['dbname'])
|
||||
);
|
||||
$dbPrivilegesGranted = (int) $query->fetchObject()->matches === count($dbPrivileges);
|
||||
}
|
||||
|
||||
$tablePrivilegesGranted = true;
|
||||
if (false === empty($tablePrivileges)) {
|
||||
$tableCondition = 'table_name IN (' . join(',', array_map(array($this, 'quote'), $context)) . ')';
|
||||
$query = $this->query(
|
||||
'SELECT COUNT(*) as matches'
|
||||
. ' FROM information_schema.table_privileges'
|
||||
. ' WHERE grantee = :grantee'
|
||||
. ' AND table_schema = :dbname'
|
||||
. ' AND ' . $tableCondition
|
||||
. ' AND ' . $privilegeCondition
|
||||
. ($requireGrants ? " AND is_grantable = 'YES'" : ''),
|
||||
array(':grantee' => $grantee, ':dbname' => $this->config['dbname'])
|
||||
);
|
||||
$expectedAmountOfMatches = count($context) * count($tablePrivileges);
|
||||
$tablePrivilegesGranted = (int) $query->fetchObject()->matches === $expectedAmountOfMatches;
|
||||
}
|
||||
|
||||
if ($dbPrivilegesGranted && $tablePrivilegesGranted) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->query(
|
||||
'SELECT COUNT(*) as matches FROM information_schema.user_privileges WHERE grantee = :grantee'
|
||||
. ' AND ' . $privilegeCondition . ($requireGrants ? " AND is_grantable = 'YES'" : ''),
|
||||
array(':grantee' => $grantee)
|
||||
);
|
||||
return (int) $query->fetchObject()->matches === count($mysqlPrivileges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current user has the given privileges
|
||||
*
|
||||
* Note that database and table specific privileges (i.e. not SUPER, CREATE and CREATEROLE) are ignored
|
||||
* in case no connection to the database defined in the resource configuration has been established
|
||||
*
|
||||
* @param array $privileges The privilege names
|
||||
* @param bool $requireGrants Only return true when all privileges can be granted to others
|
||||
* @param array $context An array describing the context for which the given privileges need to apply.
|
||||
* Only one or more table names are currently supported
|
||||
* @param string $username The login name to which the passed privileges need to be granted
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkPgsqlPrivileges(
|
||||
array $privileges,
|
||||
$requireGrants = false,
|
||||
array $context = null,
|
||||
$username = null
|
||||
) {
|
||||
$privilegesGranted = true;
|
||||
if ($this->dbFromConfig) {
|
||||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach (array_intersect($privileges, array_keys($this->pgsqlGrantContexts)) as $privilege) {
|
||||
if (false === empty($context) && $this->pgsqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
} elseif ($this->pgsqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
$dbPrivileges[] = $privilege;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($dbPrivileges)) {
|
||||
$query = $this->query(
|
||||
'SELECT has_database_privilege(:user, :dbname, :privileges) AS db_privileges_granted',
|
||||
array(
|
||||
':user' => $username !== null ? $username : $this->config['username'],
|
||||
':dbname' => $this->config['dbname'],
|
||||
':privileges' => join(',', $dbPrivileges) . ($requireGrants ? ' WITH GRANT OPTION' : '')
|
||||
)
|
||||
);
|
||||
$privilegesGranted &= $query->fetchObject()->db_privileges_granted;
|
||||
}
|
||||
|
||||
if (false === empty($tablePrivileges)) {
|
||||
foreach (array_intersect($context, $this->listTables()) as $table) {
|
||||
$query = $this->query(
|
||||
'SELECT has_table_privilege(:user, :table, :privileges) AS table_privileges_granted',
|
||||
array(
|
||||
':user' => $username !== null ? $username : $this->config['username'],
|
||||
':table' => $table,
|
||||
':privileges' => join(',', $tablePrivileges) . ($requireGrants ? ' WITH GRANT OPTION' : '')
|
||||
)
|
||||
);
|
||||
$privilegesGranted &= $query->fetchObject()->table_privileges_granted;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In case we cannot check whether the user got the required db-/table-privileges due to not being
|
||||
// connected to the database defined in the resource configuration it is safe to just ignore them
|
||||
// as the chances are very high that the database is created later causing the current user being
|
||||
// the owner with ALL privileges. (Which in turn can be granted to others.)
|
||||
}
|
||||
|
||||
if (array_search('CREATE', $privileges) !== false) {
|
||||
$query = $this->query(
|
||||
'select rolcreatedb from pg_roles where rolname = :user',
|
||||
array(':user' => $username !== null ? $username : $this->config['username'])
|
||||
);
|
||||
$privilegesGranted &= $query->fetchColumn() !== false;
|
||||
}
|
||||
|
||||
if (array_search('CREATEROLE', $privileges) !== false) {
|
||||
$query = $this->query(
|
||||
'select rolcreaterole from pg_roles where rolname = :user',
|
||||
array(':user' => $username !== null ? $username : $this->config['username'])
|
||||
);
|
||||
$privilegesGranted &= $query->fetchColumn() !== false;
|
||||
}
|
||||
|
||||
if (array_search('SUPER', $privileges) !== false) {
|
||||
$query = $this->query(
|
||||
'select rolsuper from pg_roles where rolname = :user',
|
||||
array(':user' => $username !== null ? $username : $this->config['username'])
|
||||
);
|
||||
$privilegesGranted &= $query->fetchColumn() !== false;
|
||||
}
|
||||
|
||||
return $privilegesGranted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Utils;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class EnableModuleStep extends Step
|
||||
{
|
||||
protected $modulePaths;
|
||||
|
||||
protected $moduleName;
|
||||
|
||||
protected $error;
|
||||
|
||||
public function __construct($moduleName)
|
||||
{
|
||||
$this->moduleName = $moduleName;
|
||||
|
||||
$this->modulePaths = array();
|
||||
if (($appModulePath = realpath(Icinga::app()->getApplicationDir() . '/../modules')) !== false) {
|
||||
$this->modulePaths[] = $appModulePath;
|
||||
}
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
try {
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
$moduleManager->detectInstalledModules($this->modulePaths);
|
||||
$moduleManager->enableModule($this->moduleName);
|
||||
} catch (Exception $e) {
|
||||
$this->error = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->error = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
// Enabling a module is like a implicit action, which does not need to be shown to the user...
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
if ($this->error === false) {
|
||||
return '<p>' . sprintf(mt('setup', 'Module "%s" has been successfully enabled.'), $this->moduleName) . '</p>';
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt('setup', 'Module "%s" could not be enabled. An error occured:');
|
||||
return '<p class="error">' . sprintf($message, $this->moduleName) . '</p>'
|
||||
. '<p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Utils;
|
||||
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class MakeDirStep extends Step
|
||||
{
|
||||
protected $paths;
|
||||
|
||||
protected $dirmode;
|
||||
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* @param array $paths
|
||||
* @param int $dirmode
|
||||
*/
|
||||
public function __construct($paths, $dirmode)
|
||||
{
|
||||
$this->paths = $paths;
|
||||
$this->dirmode = $dirmode;
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
public function apply()
|
||||
{
|
||||
$success = true;
|
||||
foreach ($this->paths as $path) {
|
||||
if (false === file_exists($path)) {
|
||||
if (false === @mkdir($path)) {
|
||||
$this->errors[$path] = error_get_last();
|
||||
$success = false;
|
||||
} else {
|
||||
$this->errors[$path] = null;
|
||||
$old = umask(0);
|
||||
chmod($path, $this->dirmode);
|
||||
umask($old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
public function getSummary()
|
||||
{
|
||||
// This step is usually being used for directories which are required for the configuration but
|
||||
// are not defined in any way by the user. So there is no need to show a summary for this step.
|
||||
}
|
||||
|
||||
public function getReport()
|
||||
{
|
||||
$okMessage = mt('setup', 'Directory "%s" in "%s" has been successfully created.');
|
||||
$failMessage = mt('setup', 'Unable to create directory "%s" in "%s". An error occured:');
|
||||
|
||||
$report = '';
|
||||
foreach ($this->paths as $path) {
|
||||
if (array_key_exists($path, $this->errors)) {
|
||||
if (is_array($this->errors[$path])) {
|
||||
$report .= '<p class="error">' . sprintf($failMessage, basename($path), dirname($path)) . '</p>'
|
||||
. '<p>' . $this->errors[$path]['message'] . '</p>';
|
||||
} else {
|
||||
$report .= '<p>' . sprintf($okMessage, basename($path), dirname($path)) . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $report;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup\Web\Form\Validator;
|
||||
|
||||
use Exception;
|
||||
use Zend_Validate_Abstract;
|
||||
use Icinga\Util\File;
|
||||
|
||||
/**
|
||||
* Validator that checks if a token matches with the contents of a corresponding token-file
|
||||
*/
|
||||
class TokenValidator extends Zend_Validate_Abstract
|
||||
{
|
||||
/**
|
||||
* The path to the token file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tokenPath;
|
||||
|
||||
/**
|
||||
* Create a new TokenValidator
|
||||
*
|
||||
* @param string $tokenPath The path to the token-file
|
||||
*/
|
||||
public function __construct($tokenPath)
|
||||
{
|
||||
$this->tokenPath = $tokenPath;
|
||||
$this->_messageTemplates = array(
|
||||
'TOKEN_FILE_ERROR' => sprintf(
|
||||
mt('setup', 'Cannot validate token: %s (%s)'),
|
||||
$tokenPath,
|
||||
'%value%'
|
||||
),
|
||||
'TOKEN_FILE_EMPTY' => sprintf(
|
||||
mt('setup', 'Cannot validate token, file "%s" is empty. Please define a token.'),
|
||||
$tokenPath
|
||||
),
|
||||
'TOKEN_FILE_PUBLIC' => sprintf(
|
||||
mt('setup', 'Cannot validate token, file "%s" must only be accessible by the webserver\'s user.'),
|
||||
$tokenPath
|
||||
),
|
||||
'TOKEN_INVALID' => mt('setup', 'Invalid token supplied.')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given token with the one in the token-file
|
||||
*
|
||||
* @param string $value The token to validate
|
||||
* @param null $context The form context (ignored)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value, $context = null)
|
||||
{
|
||||
$tokenStats = @stat($this->tokenPath);
|
||||
if (($tokenStats['mode'] & 4) === 4) {
|
||||
$this->_error('TOKEN_FILE_PUBLIC');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$file = new File($this->tokenPath);
|
||||
$expectedToken = trim($file->fgets());
|
||||
} catch (Exception $e) {
|
||||
$msg = $e->getMessage();
|
||||
$this->_error('TOKEN_FILE_ERROR', substr($msg, strpos($msg, ']: ') + 3));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($expectedToken)) {
|
||||
$this->_error('TOKEN_FILE_EMPTY');
|
||||
return false;
|
||||
} elseif ($value !== $expectedToken) {
|
||||
$this->_error('TOKEN_INVALID');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,571 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use PDOException;
|
||||
use Zend_Version;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Wizard;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Form\ModulePage;
|
||||
use Icinga\Module\Setup\Form\WelcomePage;
|
||||
use Icinga\Module\Setup\Form\SummaryPage;
|
||||
use Icinga\Module\Setup\Form\DbResourcePage;
|
||||
use Icinga\Module\Setup\Form\PreferencesPage;
|
||||
use Icinga\Module\Setup\Form\AuthBackendPage;
|
||||
use Icinga\Module\Setup\Form\AdminAccountPage;
|
||||
use Icinga\Module\Setup\Form\LdapDiscoveryPage;
|
||||
use Icinga\Module\Setup\Form\LdapDiscoveryConfirmPage;
|
||||
use Icinga\Module\Setup\Form\LdapResourcePage;
|
||||
use Icinga\Module\Setup\Form\RequirementsPage;
|
||||
use Icinga\Module\Setup\Form\GeneralConfigPage;
|
||||
use Icinga\Module\Setup\Form\AuthenticationPage;
|
||||
use Icinga\Module\Setup\Form\DatabaseCreationPage;
|
||||
use Icinga\Module\Setup\Steps\DatabaseStep;
|
||||
use Icinga\Module\Setup\Steps\GeneralConfigStep;
|
||||
use Icinga\Module\Setup\Steps\ResourceStep;
|
||||
use Icinga\Module\Setup\Steps\AuthenticationStep;
|
||||
use Icinga\Module\Setup\Utils\MakeDirStep;
|
||||
use Icinga\Module\Setup\Utils\DbTool;
|
||||
|
||||
/**
|
||||
* Icinga Web 2 Setup Wizard
|
||||
*/
|
||||
class WebWizard extends Wizard implements SetupWizard
|
||||
{
|
||||
/**
|
||||
* The privileges required by Icinga Web 2 to setup the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseSetupPrivileges = array(
|
||||
'CREATE',
|
||||
'ALTER',
|
||||
'REFERENCES',
|
||||
'CREATE USER', // MySQL
|
||||
'CREATEROLE' // PostgreSQL
|
||||
);
|
||||
|
||||
/**
|
||||
* The privileges required by Icinga Web 2 to operate the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseUsagePrivileges = array(
|
||||
'SELECT',
|
||||
'INSERT',
|
||||
'UPDATE',
|
||||
'DELETE',
|
||||
'EXECUTE',
|
||||
'TEMPORARY', // PostgreSql
|
||||
'CREATE TEMPORARY TABLES' // MySQL
|
||||
);
|
||||
|
||||
/**
|
||||
* The database tables operated by Icinga Web 2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseTables = array(
|
||||
'icingaweb_group',
|
||||
'icingaweb_group_membership',
|
||||
'icingaweb_user',
|
||||
'icingaweb_user_preference'
|
||||
);
|
||||
|
||||
/**
|
||||
* @see Wizard::init()
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$this->addPage(new WelcomePage());
|
||||
$this->addPage(new RequirementsPage());
|
||||
$this->addPage(new AuthenticationPage());
|
||||
$this->addPage(new PreferencesPage());
|
||||
$this->addPage(new DbResourcePage());
|
||||
$this->addPage(new LdapDiscoveryPage());
|
||||
$this->addPage(new LdapDiscoveryConfirmPage());
|
||||
$this->addPage(new LdapResourcePage());
|
||||
$this->addPage(new AuthBackendPage());
|
||||
$this->addPage(new AdminAccountPage());
|
||||
$this->addPage(new GeneralConfigPage());
|
||||
$this->addPage(new DatabaseCreationPage());
|
||||
$this->addPage(new ModulePage());
|
||||
$this->addPage(new SummaryPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::setupPage()
|
||||
*/
|
||||
public function setupPage(Form $page, Request $request)
|
||||
{
|
||||
if ($page->getName() === 'setup_requirements') {
|
||||
$page->setRequirements($this->getRequirements());
|
||||
} elseif ($page->getName() === 'setup_preferences_type') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->create()->showDatabaseNote();
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_authentication_backend') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->setResourceConfig($this->getPageData('setup_db_resource'));
|
||||
} elseif ($authData['type'] === 'ldap') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
|
||||
|
||||
$suggestions = $this->getPageData('setup_ldap_discovery_confirm');
|
||||
if (isset($suggestions['backend'])) {
|
||||
$page->populate($suggestions['backend']);
|
||||
}
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_ldap_discovery_confirm') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_discovery'));
|
||||
} elseif ($page->getName() === 'setup_admin_account') {
|
||||
$page->setBackendConfig($this->getPageData('setup_authentication_backend'));
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->setResourceConfig($this->getPageData('setup_db_resource'));
|
||||
} elseif ($authData['type'] === 'ldap') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_database_creation') {
|
||||
$page->setDatabaseSetupPrivileges($this->databaseSetupPrivileges);
|
||||
$page->setDatabaseUsagePrivileges($this->databaseUsagePrivileges);
|
||||
$page->setResourceConfig($this->getPageData('setup_db_resource'));
|
||||
} elseif ($page->getName() === 'setup_summary') {
|
||||
$page->setSubjectTitle('Icinga Web 2');
|
||||
$page->setSummary($this->getSetup()->getSummary());
|
||||
} elseif ($page->getName() === 'setup_db_resource') {
|
||||
$ldapData = $this->getPageData('setup_ldap_resource');
|
||||
if ($ldapData !== null && $request->getPost('name') === $ldapData['name']) {
|
||||
$page->addError(
|
||||
mt('setup', 'The given resource name must be unique and is already in use by the LDAP resource')
|
||||
);
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_ldap_resource') {
|
||||
$dbData = $this->getPageData('setup_db_resource');
|
||||
if ($dbData !== null && $request->getPost('name') === $dbData['name']) {
|
||||
$page->addError(
|
||||
mt('setup', 'The given resource name must be unique and is already in use by the database resource')
|
||||
);
|
||||
}
|
||||
|
||||
$suggestion = $this->getPageData('setup_ldap_discovery_confirm');
|
||||
if (isset($suggestion['resource'])) {
|
||||
$page->populate($suggestion['resource']);
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_authentication_type' && $this->getDirection() === static::FORWARD) {
|
||||
$authData = $this->getPageData($page->getName());
|
||||
if ($authData !== null && $request->getPost('type') !== $authData['type']) {
|
||||
// Drop any existing page data in case the authentication type has changed,
|
||||
// otherwise it will conflict with other forms that depend on this one
|
||||
$pageData = & $this->getPageData();
|
||||
unset($pageData['setup_admin_account']);
|
||||
unset($pageData['setup_authentication_backend']);
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_modules') {
|
||||
$page->setPageData($this->getPageData());
|
||||
$page->handleRequest($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::getNewPage()
|
||||
*/
|
||||
protected function getNewPage($requestedPage, Form $originPage)
|
||||
{
|
||||
$skip = false;
|
||||
$newPage = parent::getNewPage($requestedPage, $originPage);
|
||||
if ($newPage->getName() === 'setup_db_resource') {
|
||||
$prefData = $this->getPageData('setup_preferences_type');
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
$skip = $prefData['type'] !== 'db' && $authData['type'] !== 'db';
|
||||
} elseif ($newPage->getname() === 'setup_ldap_discovery') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
$skip = $authData['type'] !== 'ldap';
|
||||
} elseif ($newPage->getName() === 'setup_ldap_discovery_confirm') {
|
||||
$skip = false === $this->hasPageData('setup_ldap_discovery');
|
||||
} elseif ($newPage->getName() === 'setup_ldap_resource') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
$skip = $authData['type'] !== 'ldap';
|
||||
} elseif ($newPage->getName() === 'setup_database_creation') {
|
||||
if (($config = $this->getPageData('setup_db_resource')) !== null && ! $config['skip_validation']) {
|
||||
$db = new DbTool($config);
|
||||
|
||||
try {
|
||||
$db->connectToDb(); // Are we able to login on the database?
|
||||
if (array_search(key($this->databaseTables), $db->listTables()) === false) {
|
||||
// In case the database schema does not yet exist the user
|
||||
// needs the privileges to create and setup the database
|
||||
$skip = $db->checkPrivileges($this->databaseSetupPrivileges, $this->databaseTables);
|
||||
} else {
|
||||
// In case the database schema exists the user needs the required privileges
|
||||
// to operate the database, if those are missing we ask for another user
|
||||
$skip = $db->checkPrivileges($this->databaseUsagePrivileges, $this->databaseTables);
|
||||
}
|
||||
} catch (PDOException $_) {
|
||||
try {
|
||||
$db->connectToHost(); // Are we able to login on the server?
|
||||
// It is not possible to reliably determine whether a database exists or not if a user can't
|
||||
// log in to the database, so we just require the user to be able to create the database
|
||||
$skip = $db->checkPrivileges($this->databaseSetupPrivileges, $this->databaseTables);
|
||||
} catch (PDOException $_) {
|
||||
// We are NOT able to login on the server..
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($skip) {
|
||||
if ($this->hasPageData($newPage->getName())) {
|
||||
$pageData = & $this->getPageData();
|
||||
unset($pageData[$newPage->getName()]);
|
||||
}
|
||||
|
||||
$pages = $this->getPages();
|
||||
if ($this->getDirection() === static::FORWARD) {
|
||||
$nextPage = $pages[array_search($newPage, $pages, true) + 1];
|
||||
$newPage = $this->getNewPage($nextPage->getName(), $newPage);
|
||||
} else { // $this->getDirection() === static::BACKWARD
|
||||
$previousPage = $pages[array_search($newPage, $pages, true) - 1];
|
||||
$newPage = $this->getNewPage($previousPage->getName(), $newPage);
|
||||
}
|
||||
}
|
||||
|
||||
return $newPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::addButtons()
|
||||
*/
|
||||
protected function addButtons(Form $page)
|
||||
{
|
||||
parent::addButtons($page);
|
||||
|
||||
$pages = $this->getPages();
|
||||
$index = array_search($page, $pages, true);
|
||||
if ($index === 0) {
|
||||
$page->getElement(static::BTN_NEXT)->setLabel(mt('setup', 'Start', 'setup.welcome.btn.next'));
|
||||
} elseif ($index === count($pages) - 1) {
|
||||
$page->getElement(static::BTN_NEXT)->setLabel(mt('setup', 'Setup Icinga Web 2', 'setup.summary.btn.finish'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::clearSession()
|
||||
*/
|
||||
public function clearSession()
|
||||
{
|
||||
parent::clearSession();
|
||||
$this->getPage('setup_modules')->clearSession();
|
||||
|
||||
$tokenPath = Config::resolvePath('setup.token');
|
||||
if (file_exists($tokenPath)) {
|
||||
@unlink($tokenPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getSetup()
|
||||
*/
|
||||
public function getSetup()
|
||||
{
|
||||
$pageData = $this->getPageData();
|
||||
$setup = new Setup();
|
||||
|
||||
if (isset($pageData['setup_db_resource'])
|
||||
&& ! $pageData['setup_db_resource']['skip_validation']
|
||||
&& (false === isset($pageData['setup_database_creation'])
|
||||
|| ! $pageData['setup_database_creation']['skip_validation']
|
||||
)
|
||||
) {
|
||||
$setup->addStep(
|
||||
new DatabaseStep(array(
|
||||
'tables' => $this->databaseTables,
|
||||
'privileges' => $this->databaseUsagePrivileges,
|
||||
'resourceConfig' => $pageData['setup_db_resource'],
|
||||
'adminName' => isset($pageData['setup_database_creation']['username'])
|
||||
? $pageData['setup_database_creation']['username']
|
||||
: null,
|
||||
'adminPassword' => isset($pageData['setup_database_creation']['password'])
|
||||
? $pageData['setup_database_creation']['password']
|
||||
: null
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
$setup->addStep(
|
||||
new GeneralConfigStep(array(
|
||||
'generalConfig' => $pageData['setup_general_config'],
|
||||
'preferencesType' => $pageData['setup_preferences_type']['type'],
|
||||
'preferencesResource' => isset($pageData['setup_db_resource']['name'])
|
||||
? $pageData['setup_db_resource']['name']
|
||||
: null
|
||||
))
|
||||
);
|
||||
|
||||
$adminAccountType = $pageData['setup_admin_account']['user_type'];
|
||||
$adminAccountData = array('username' => $pageData['setup_admin_account'][$adminAccountType]);
|
||||
if ($adminAccountType === 'new_user' && ! $pageData['setup_db_resource']['skip_validation']
|
||||
&& (false === isset($pageData['setup_database_creation'])
|
||||
|| ! $pageData['setup_database_creation']['skip_validation']
|
||||
)
|
||||
) {
|
||||
$adminAccountData['resourceConfig'] = $pageData['setup_db_resource'];
|
||||
$adminAccountData['password'] = $pageData['setup_admin_account']['new_user_password'];
|
||||
}
|
||||
$authType = $pageData['setup_authentication_type']['type'];
|
||||
$setup->addStep(
|
||||
new AuthenticationStep(array(
|
||||
'adminAccountData' => $adminAccountData,
|
||||
'backendConfig' => $pageData['setup_authentication_backend'],
|
||||
'resourceName' => $authType === 'db' ? $pageData['setup_db_resource']['name'] : (
|
||||
$authType === 'ldap' ? $pageData['setup_ldap_resource']['name'] : null
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
if (isset($pageData['setup_db_resource']) || isset($pageData['setup_ldap_resource'])) {
|
||||
$setup->addStep(
|
||||
new ResourceStep(array(
|
||||
'dbResourceConfig' => isset($pageData['setup_db_resource'])
|
||||
? array_diff_key($pageData['setup_db_resource'], array('skip_validation' => null))
|
||||
: null,
|
||||
'ldapResourceConfig' => isset($pageData['setup_ldap_resource'])
|
||||
? array_diff_key($pageData['setup_ldap_resource'], array('skip_validation' => null))
|
||||
: null
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
$configDir = $this->getConfigDir();
|
||||
$setup->addStep(
|
||||
new MakeDirStep(
|
||||
array(
|
||||
$configDir . '/modules',
|
||||
$configDir . '/preferences',
|
||||
$configDir . '/enabledModules'
|
||||
),
|
||||
0775
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($this->getPage('setup_modules')->setPageData($this->getPageData())->getWizards() as $wizard) {
|
||||
if ($wizard->isFinished()) {
|
||||
$setup->addSteps($wizard->getSetup()->getSteps());
|
||||
}
|
||||
}
|
||||
|
||||
return $setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getRequirements()
|
||||
*/
|
||||
public function getRequirements()
|
||||
{
|
||||
$requirements = new Requirements();
|
||||
|
||||
$phpVersion = Platform::getPhpVersion();
|
||||
$requirements->addMandatory(
|
||||
mt('setup', 'PHP Version'),
|
||||
mt(
|
||||
'setup',
|
||||
'Running Icinga Web 2 requires PHP version 5.3.2. Advanced features'
|
||||
. ' like the built-in web server require PHP version 5.4.'
|
||||
),
|
||||
version_compare($phpVersion, '5.3.2', '>='),
|
||||
sprintf(mt('setup', 'You are running PHP version %s.'), $phpVersion)
|
||||
);
|
||||
|
||||
// The only reason for requiring 1.12.2 is a bug in Zend_Form_Decorator_ViewHelper in older versions causing a
|
||||
// Zend_Form_Element_Button's value not being rendered. Feel free to adjust this in case we require an earlier
|
||||
// version!
|
||||
$zendVersion = Zend_Version::VERSION;
|
||||
$requirements->addMandatory(
|
||||
mt('setup', 'Zend Framework 1'),
|
||||
mt(
|
||||
'setup',
|
||||
'Icinga Web 2 requires at least Zend Framework 1.12.2 to work properly.'
|
||||
),
|
||||
Zend_Version::compareVersion('1.12.2') !== 1,
|
||||
sprintf(mt('setup', 'You have got Zend Framwork %s installed.'), $zendVersion)
|
||||
);
|
||||
|
||||
$defaultTimezone = Platform::getPhpConfig('date.timezone');
|
||||
$requirements->addMandatory(
|
||||
mt('setup', 'Default Timezone'),
|
||||
mt('setup', 'It is required that a default timezone has been set using date.timezone in php.ini.'),
|
||||
$defaultTimezone,
|
||||
$defaultTimezone ? sprintf(mt('setup', 'Your default timezone is: %s'), $defaultTimezone) : (
|
||||
mt('setup', 'You did not define a default timezone.')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'Linux Platform'),
|
||||
mt(
|
||||
'setup',
|
||||
'Icinga Web 2 is developed for and tested on Linux. While we cannot'
|
||||
. ' guarantee they will, other platforms may also perform as well.'
|
||||
),
|
||||
Platform::isLinux(),
|
||||
sprintf(mt('setup', 'You are running PHP on a %s system.'), Platform::getOperatingSystemName())
|
||||
);
|
||||
|
||||
$requirements->addMandatory(
|
||||
mt('setup', 'PHP Module: OpenSSL'),
|
||||
mt('setup', 'The PHP module for OpenSSL is required to generate cryptographically safe password salts.'),
|
||||
Platform::extensionLoaded('openssl'),
|
||||
Platform::extensionLoaded('openssl') ? mt('setup', 'The PHP module for OpenSSL is available.') : (
|
||||
mt('setup', 'The PHP module for OpenSSL is missing.')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: JSON'),
|
||||
mt('setup', 'The JSON module for PHP is required for various export functionalities as well as APIs.'),
|
||||
Platform::extensionLoaded('json'),
|
||||
Platform::extensionLoaded('json') ? mt('setup', 'The PHP module JSON is available.') : (
|
||||
mt('setup', 'The PHP module JSON is missing.')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: LDAP'),
|
||||
mt('setup', 'If you\'d like to authenticate users using LDAP the corresponding PHP module is required'),
|
||||
Platform::extensionLoaded('ldap'),
|
||||
Platform::extensionLoaded('ldap') ? mt('setup', 'The PHP module LDAP is available') : (
|
||||
mt('setup', 'The PHP module LDAP is missing')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: INTL'),
|
||||
mt(
|
||||
'setup',
|
||||
'If you want your users to benefit from language, timezone and date/time'
|
||||
. ' format negotiation, the INTL module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('intl'),
|
||||
Platform::extensionLoaded('intl') ? mt('setup', 'The PHP module INTL is available') : (
|
||||
mt('setup', 'The PHP module INTL is missing')
|
||||
)
|
||||
);
|
||||
|
||||
// TODO(6172): Remove this requirement once we do not ship dompdf with Icinga Web 2 anymore
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: DOM'),
|
||||
mt('setup', 'To be able to export views and reports to PDF, the DOM module for PHP is required.'),
|
||||
Platform::extensionLoaded('dom'),
|
||||
Platform::extensionLoaded('dom') ? mt('setup', 'The PHP module DOM is available') : (
|
||||
mt('setup', 'The PHP module DOM is missing')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: GD'),
|
||||
mt(
|
||||
'setup',
|
||||
'In case you want icons and graphs being exported to PDF'
|
||||
. ' as well, you\'ll need the GD extension for PHP.'
|
||||
),
|
||||
Platform::extensionLoaded('gd'),
|
||||
Platform::extensionLoaded('gd') ? mt('setup', 'The PHP module GD is available') : (
|
||||
mt('setup', 'The PHP module GD is missing')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: PDO-MySQL'),
|
||||
mt(
|
||||
'setup',
|
||||
'Is Icinga Web 2 supposed to access a MySQL database the PDO-MySQL module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('mysql'),
|
||||
Platform::extensionLoaded('mysql') ? mt('setup', 'The PHP module PDO-MySQL is available.') : (
|
||||
mt('setup', 'The PHP module PDO-MySQL is missing.')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'PHP Module: PDO-PostgreSQL'),
|
||||
mt(
|
||||
'setup',
|
||||
'Is Icinga Web 2 supposed to access a PostgreSQL database'
|
||||
. ' the PDO-PostgreSQL module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('pgsql'),
|
||||
Platform::extensionLoaded('pgsql') ? mt('setup', 'The PHP module PDO-PostgreSQL is available.') : (
|
||||
mt('setup', 'The PHP module PDO-PostgreSQL is missing.')
|
||||
)
|
||||
);
|
||||
|
||||
// TODO(7464): Re-enable or remove this entirely once a decision has been made regarding shipping Zend with Iw2
|
||||
/*$mysqlAdapterFound = Platform::zendClassExists('Zend_Db_Adapter_Pdo_Mysql');
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'Zend Database Adapter For MySQL'),
|
||||
mt('setup', 'The Zend database adapter for MySQL is required to access a MySQL database.'),
|
||||
$mysqlAdapterFound,
|
||||
$mysqlAdapterFound ? mt('setup', 'The Zend database adapter for MySQL is available.') : (
|
||||
mt('setup', 'The Zend database adapter for MySQL is missing.')
|
||||
)
|
||||
);
|
||||
|
||||
$pgsqlAdapterFound = Platform::zendClassExists('Zend_Db_Adapter_Pdo_Pgsql');
|
||||
$requirements->addOptional(
|
||||
mt('setup', 'Zend Database Adapter For PostgreSQL'),
|
||||
mt('setup', 'The Zend database adapter for PostgreSQL is required to access a PostgreSQL database.'),
|
||||
$pgsqlAdapterFound,
|
||||
$pgsqlAdapterFound ? mt('setup', 'The Zend database adapter for PostgreSQL is available.') : (
|
||||
mt('setup', 'The Zend database adapter for PostgreSQL is missing.')
|
||||
)
|
||||
);*/
|
||||
|
||||
$configDir = $this->getConfigDir();
|
||||
$requirements->addMandatory(
|
||||
mt('setup', 'Writable Config Directory'),
|
||||
mt(
|
||||
'setup',
|
||||
'The Icinga Web 2 configuration directory defaults to "/etc/icingaweb", if' .
|
||||
' not explicitly set in the environment variable "ICINGAWEB_CONFIGDIR".'
|
||||
),
|
||||
is_writable($configDir),
|
||||
sprintf(
|
||||
is_writable($configDir) ? mt('setup', 'The current configuration directory is writable: %s') : (
|
||||
mt('setup', 'The current configuration directory is not writable: %s')
|
||||
),
|
||||
$configDir
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($this->getPage('setup_modules')->setPageData($this->getPageData())->getWizards() as $wizard) {
|
||||
$requirements->merge($wizard->getRequirements()->allOptional());
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration directory of Icinga Web 2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigDir()
|
||||
{
|
||||
if (array_key_exists('ICINGAWEB_CONFIGDIR', $_SERVER)) {
|
||||
$configDir = $_SERVER['ICINGAWEB_CONFIGDIR'];
|
||||
} else {
|
||||
$configDir = '/etc/icingaweb';
|
||||
}
|
||||
|
||||
$canonical = realpath($configDir);
|
||||
return $canonical ? $canonical : $configDir;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue