Merge branch 'master' into bugfix/improve-form-notifications-8983
Conflicts: library/Icinga/Web/Form.php modules/setup/application/forms/AdminAccountPage.php
This commit is contained in:
commit
47fbc24f17
|
@ -53,7 +53,7 @@ class ConfigController extends Controller
|
|||
));
|
||||
$tabs->add('usergroupbackend', array(
|
||||
'title' => $this->translate('Configure how users are associated with groups by Icinga Web 2'),
|
||||
'label' => $this->translate('Usergroup Backends'),
|
||||
'label' => $this->translate('User Group Backends'),
|
||||
'url' => 'usergroupbackend/list'
|
||||
));
|
||||
return $tabs;
|
||||
|
|
|
@ -6,6 +6,7 @@ use Icinga\Web\Url;
|
|||
use Icinga\Web\Widget\Tab;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Forms\PreferenceForm;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\User\Preferences\PreferencesStore;
|
||||
|
||||
/**
|
||||
|
@ -38,13 +39,16 @@ class PreferenceController extends BasePreferenceController
|
|||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$storeConfig = Config::app()->getSection('preferences');
|
||||
|
||||
$config = Config::app()->getSection('global');
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$form = new PreferenceForm();
|
||||
$form->setPreferences($user->getPreferences());
|
||||
if ($storeConfig->get('store', 'ini') !== 'none') {
|
||||
$form->setStore(PreferencesStore::create($storeConfig, $user));
|
||||
if ($config->get('config_backend', 'ini') !== 'none') {
|
||||
$form->setStore(PreferencesStore::create(new ConfigObject(array(
|
||||
'store' => $config->get('config_backend', 'ini'),
|
||||
'resource' => $config->config_resource
|
||||
)), $user));
|
||||
}
|
||||
$form->handleRequest();
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ class UsergroupbackendController extends Controller
|
|||
));
|
||||
$tabs->add('usergroupbackend', array(
|
||||
'title' => $this->translate('Configure how users are associated with groups by Icinga Web 2'),
|
||||
'label' => $this->translate('Usergroup Backends'),
|
||||
'label' => $this->translate('User Group Backends'),
|
||||
'url' => 'usergroupbackend/list'
|
||||
));
|
||||
return $tabs;
|
||||
|
|
|
@ -7,7 +7,6 @@ use Icinga\Application\Icinga;
|
|||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
|
||||
/**
|
||||
* Form class to modify the general application configuration
|
||||
*/
|
||||
|
@ -43,7 +42,7 @@ class ApplicationConfigForm extends Form
|
|||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'preferences_store',
|
||||
'global_config_backend',
|
||||
array(
|
||||
'required' => true,
|
||||
'autosubmit' => true,
|
||||
|
@ -55,7 +54,7 @@ class ApplicationConfigForm extends Form
|
|||
)
|
||||
)
|
||||
);
|
||||
if (isset($formData['preferences_store']) && $formData['preferences_store'] === 'db') {
|
||||
if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'db') {
|
||||
$backends = array();
|
||||
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
|
||||
if ($resource['type'] === 'db') {
|
||||
|
@ -65,7 +64,7 @@ class ApplicationConfigForm extends Form
|
|||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'preferences_resource',
|
||||
'global_config_resource',
|
||||
array(
|
||||
'required' => true,
|
||||
'multiOptions' => $backends,
|
||||
|
|
|
@ -129,16 +129,13 @@ class DbResourceForm extends Form
|
|||
*/
|
||||
public static function isValidResource(Form $form)
|
||||
{
|
||||
try {
|
||||
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
|
||||
$resource->getConnection()->getConnection();
|
||||
} catch (Exception $e) {
|
||||
$form->addError(
|
||||
$form->translate('Connectivity validation failed, connection to the given resource not possible.')
|
||||
);
|
||||
return false;
|
||||
$result = ResourceFactory::createResource(new ConfigObject($form->getValues()))->inspect();
|
||||
if ($result->hasError()) {
|
||||
$form->addError(sprintf($form->translate('Connectivity validation failed: %s'), $result->getError()));
|
||||
}
|
||||
|
||||
return true;
|
||||
// TODO: display diagnostics in $result->toArray() to the user
|
||||
|
||||
return ! $result->hasError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,20 +154,17 @@ class LdapResourceForm extends Form
|
|||
*/
|
||||
public static function isValidResource(Form $form)
|
||||
{
|
||||
try {
|
||||
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
|
||||
$resource->connect();
|
||||
$resource->bind();
|
||||
} catch (Exception $e) {
|
||||
$msg = $form->translate('Connectivity validation failed, connection to the given resource not possible.');
|
||||
if (($error = $e->getMessage())) {
|
||||
$msg .= ' (' . $error . ')';
|
||||
}
|
||||
|
||||
$form->addError($msg);
|
||||
return false;
|
||||
$result = ResourceFactory::createResource(new ConfigObject($form->getValues()))->inspect();
|
||||
if ($result->hasError()) {
|
||||
$form->addError(sprintf(
|
||||
'%s (%s)',
|
||||
$form->translate('Connectivity validation failed, connection to the given resource not possible.'),
|
||||
$result->getError()
|
||||
));
|
||||
}
|
||||
|
||||
return true;
|
||||
// TODO: display diagnostics in $result->toArray() to the user
|
||||
|
||||
return ! $result->hasError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,18 +105,15 @@ class DbBackendForm extends Form
|
|||
*/
|
||||
public static function isValidUserBackend(Form $form)
|
||||
{
|
||||
try {
|
||||
$dbUserBackend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig()));
|
||||
if ($dbUserBackend->select()->where('is_active', true)->count() < 1) {
|
||||
$form->addError($form->translate('No active users found under the specified database backend'));
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$form->addError(sprintf($form->translate('Using the specified backend failed: %s'), $e->getMessage()));
|
||||
return false;
|
||||
$backend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig()));
|
||||
$result = $backend->inspect();
|
||||
if ($result->hasError()) {
|
||||
$form->addError(sprintf($form->translate('Using the specified backend failed: %s'), $result->getError()));
|
||||
}
|
||||
|
||||
return true;
|
||||
// TODO: display diagnostics in $result->toArray() to the user
|
||||
|
||||
return ! $result->hasError();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
namespace Icinga\Forms\Config\UserBackend;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Authentication\User\LdapUserBackend;
|
||||
use Icinga\Data\Inspection;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
|
@ -184,22 +186,16 @@ class LdapBackendForm extends Form
|
|||
*/
|
||||
public static function isValidUserBackend(Form $form)
|
||||
{
|
||||
try {
|
||||
$ldapUserBackend = UserBackend::create(null, new ConfigObject($form->getValues()));
|
||||
$ldapUserBackend->assertAuthenticationPossible();
|
||||
} catch (AuthenticationException $e) {
|
||||
if (($previous = $e->getPrevious()) !== null) {
|
||||
$form->addError($previous->getMessage());
|
||||
} else {
|
||||
$form->addError($e->getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$form->addError(sprintf($form->translate('Unable to validate authentication: %s'), $e->getMessage()));
|
||||
return false;
|
||||
/**
|
||||
* @var $result Inspection
|
||||
*/
|
||||
$result = UserBackend::create(null, new ConfigObject($form->getValues()))->inspect();
|
||||
if ($result->hasError()) {
|
||||
$form->addError($result->getError());
|
||||
}
|
||||
|
||||
return true;
|
||||
// TODO: display diagnostics in $result->toArray() to the user
|
||||
|
||||
return ! $result->hasError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,14 +35,14 @@ Please contact your distribution packagers.
|
|||
You need to add the Icinga repository to your package management configuration for installing Icinga Web 2.
|
||||
Below is a list with examples for various distributions.
|
||||
|
||||
Debian (debmon):
|
||||
**Debian (debmon)**:
|
||||
````
|
||||
wget -O - http://debmon.org/debmon/repo.key 2>/dev/null | apt-key add -
|
||||
echo 'deb http://debmon.org/debmon debmon-wheezy main' >/etc/apt/sources.list.d/debmon.list
|
||||
apt-get update
|
||||
````
|
||||
|
||||
Ubuntu Trusty:
|
||||
**Ubuntu Trusty**:
|
||||
````
|
||||
wget -O - http://packages.icinga.org/icinga.key | apt-key add -
|
||||
add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-trusty main'
|
||||
|
@ -51,59 +51,72 @@ apt-get update
|
|||
|
||||
For other Ubuntu versions just replace trusty with your distribution's code name.
|
||||
|
||||
RHEL and CentOS:
|
||||
**RHEL and CentOS**:
|
||||
````
|
||||
rpm --import http://packages.icinga.org/icinga.key
|
||||
curl -o /etc/yum.repos.d/ICINGA-release.repo http://packages.icinga.org/epel/ICINGA-release.repo
|
||||
yum makecache
|
||||
````
|
||||
|
||||
Fedora:
|
||||
**Fedora**:
|
||||
````
|
||||
rpm --import http://packages.icinga.org/icinga.key
|
||||
curl -o /etc/yum.repos.d/ICINGA-release.repo http://packages.icinga.org/fedora/ICINGA-release.repo
|
||||
yum makecache
|
||||
````
|
||||
|
||||
SLES 11:
|
||||
**SLES 11**:
|
||||
````
|
||||
zypper ar http://packages.icinga.org/SUSE/ICINGA-release-11.repo
|
||||
zypper ref
|
||||
````
|
||||
|
||||
SLES 12:
|
||||
**SLES 12**:
|
||||
````
|
||||
zypper ar http://packages.icinga.org/SUSE/ICINGA-release.repo
|
||||
zypper ref
|
||||
````
|
||||
|
||||
openSUSE:
|
||||
**openSUSE**:
|
||||
````
|
||||
zypper ar http://packages.icinga.org/openSUSE/ICINGA-release.repo
|
||||
zypper ref
|
||||
````
|
||||
|
||||
#### <a id="package-repositories-rhel-notes"></a> RHEL/CentOS Notes
|
||||
|
||||
The packages for RHEL/CentOS depend on other packages which are distributed as part of the
|
||||
[EPEL repository](http://fedoraproject.org/wiki/EPEL). Please make sure to enable this repository by following
|
||||
[these instructions](http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F).
|
||||
|
||||
> Please note that installing Icinga Web 2 on **RHEL/CentOS 5** is not supported due to EOL versions of PHP and
|
||||
> PostgreSQL.
|
||||
|
||||
#### <a id="package-repositories-wheezy-notes"></a> Debian wheezy Notes
|
||||
|
||||
The packages for Debian wheezy depend on other packages which are distributed as part of the
|
||||
[wheezy-packports](http://backports.debian.org/) repository. Please make sure to enable this repository by following
|
||||
[these instructions](http://backports.debian.org/Instructions/).
|
||||
|
||||
### <a id="installing-from-package-example"></a> Installing Icinga Web 2
|
||||
|
||||
You can install Icinga Web 2 by using your distribution's package manager to install the `icingaweb2` package.
|
||||
Below is a list with examples for various distributions. The additional package `icingacli` is necessary
|
||||
for being able to follow further steps in this guide.
|
||||
|
||||
Debian and Ubuntu:
|
||||
**Debian and Ubuntu**:
|
||||
````
|
||||
apt-get install icingaweb2 icingacli
|
||||
````
|
||||
For Debian wheezy please read the [package repositories notes](#package-repositories-wheezy-notes).
|
||||
|
||||
RHEL, CentOS and Fedora:
|
||||
**RHEL, CentOS and Fedora**:
|
||||
````
|
||||
yum install icingaweb2 icingacli
|
||||
````
|
||||
For RHEL/CentOS please read the [package repositories notes](#package-repositories-rhel-notes).
|
||||
|
||||
SLES and openSUSE:
|
||||
**SLES and openSUSE**:
|
||||
````
|
||||
zypper install icingaweb2 icingacli
|
||||
````
|
||||
|
@ -158,12 +171,12 @@ mv icingaweb2 /usr/share/icingaweb2
|
|||
|
||||
Use `icingacli` to generate web server configuration for either Apache or nginx.
|
||||
|
||||
Apache:
|
||||
**Apache**:
|
||||
````
|
||||
./bin/icingacli setup config webserver apache --document-root /usr/share/icingaweb2/public
|
||||
````
|
||||
|
||||
nginx:
|
||||
**nginx**:
|
||||
````
|
||||
./bin/icingacli setup config webserver nginx --document-root /usr/share/icingaweb2/public
|
||||
````
|
||||
|
@ -185,29 +198,29 @@ system group. The web server user and CLI user have to be added to this system g
|
|||
|
||||
Add the system group `icingaweb2` in the first place.
|
||||
|
||||
Fedora, RHEL, CentOS, SLES and OpenSUSE:
|
||||
**Fedora, RHEL, CentOS, SLES and OpenSUSE**:
|
||||
````
|
||||
groupadd -r icingaweb2
|
||||
````
|
||||
|
||||
Debian and Ubuntu:
|
||||
**Debian and Ubuntu**:
|
||||
````
|
||||
addgroup --system icingaweb2
|
||||
````
|
||||
|
||||
Add your web server's user to the system group `icingaweb2`:
|
||||
|
||||
Fedora, RHEL and CentOS:
|
||||
**Fedora, RHEL and CentOS**:
|
||||
````
|
||||
usermod -a -G icingaweb2 apache
|
||||
````
|
||||
|
||||
SLES and OpenSUSE:
|
||||
**SLES and OpenSUSE**:
|
||||
````
|
||||
usermod -A icingaweb2 wwwrun
|
||||
````
|
||||
|
||||
Debian and Ubuntu:
|
||||
**Debian and Ubuntu**:
|
||||
````
|
||||
usermod -a -G icingaweb2 www-data
|
||||
````
|
||||
|
@ -271,3 +284,9 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
|
|||
predefined subset of filter columns. Please see the module's security
|
||||
related documentation for more details.
|
||||
|
||||
## <a id="upgrading-to-2.0.0"></a> Upgrading to Icinga Web 2 2.0.0
|
||||
|
||||
* Icinga Web 2 installations from package on RHEL/CentOS 7 now depend on `php-ZendFramework` which is available through
|
||||
the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library
|
||||
through the package `icingaweb2-vendor-zend`. After upgrading, please make sure to remove the package
|
||||
`icingaweb2-vendor-zend`.
|
||||
|
|
|
@ -14,21 +14,12 @@ BuildArch: noarch
|
|||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
|
||||
Packager: Icinga Team <info@icinga.org>
|
||||
|
||||
%if 0%{?fedora} || 0%{?rhel}
|
||||
%if 0%{?fedora} || 0%{?rhel} || 0%{?amzn}
|
||||
%define php php
|
||||
%define php_cli php-cli
|
||||
%define wwwconfigdir %{_sysconfdir}/httpd/conf.d
|
||||
%define wwwuser apache
|
||||
%if 0%{?rhel} == 5
|
||||
%define php php53
|
||||
%define php_cli php53-cli
|
||||
%else
|
||||
%define php php
|
||||
%define php_cli php-cli
|
||||
%endif
|
||||
%if 0%{?rhel} == 6
|
||||
%define zend php-ZendFramework
|
||||
%else
|
||||
%define zend %{name}-vendor-Zend
|
||||
%endif
|
||||
%define zend php-ZendFramework
|
||||
%endif
|
||||
|
||||
%if 0%{?suse_version}
|
||||
|
@ -44,6 +35,7 @@ Requires: apache2-mod_php5
|
|||
%endif
|
||||
%endif
|
||||
|
||||
%{?amzn:Requires(pre): shadow-utils}
|
||||
%{?fedora:Requires(pre): shadow-utils}
|
||||
%{?rhel:Requires(pre): shadow-utils}
|
||||
%{?suse_version:Requires(pre): pwdutils}
|
||||
|
@ -55,6 +47,7 @@ Requires: %{name}-vendor-JShrink
|
|||
Requires: %{name}-vendor-lessphp
|
||||
Requires: %{name}-vendor-Parsedown
|
||||
Requires: %{zend}
|
||||
Obsoletes: %{name}-vendor-zend
|
||||
|
||||
|
||||
%description
|
||||
|
@ -73,6 +66,7 @@ Icinga Web 2
|
|||
%package common
|
||||
Summary: Common files for Icinga Web 2 and the Icinga CLI
|
||||
Group: Applications/System
|
||||
%{?amzn:Requires(pre): shadow-utils}
|
||||
%{?fedora:Requires(pre): shadow-utils}
|
||||
%{?rhel:Requires(pre): shadow-utils}
|
||||
%{?suse_version:Requires(pre): pwdutils}
|
||||
|
@ -86,6 +80,7 @@ Summary: Icinga Web 2 PHP library
|
|||
Group: Development/Libraries
|
||||
Requires: %{php} >= 5.3.0
|
||||
Requires: %{php}-gd %{php}-intl
|
||||
%{?amzn:Requires: %{php}-pecl-imagick}
|
||||
%{?fedora:Requires: php-pecl-imagick}
|
||||
%{?rhel:Requires: php-pecl-imagick}
|
||||
%{?suse_version:Requires: %{php}-gettext %{php}-json %{php}-openssl %{php}-posix}
|
||||
|
@ -99,6 +94,7 @@ Summary: Icinga CLI
|
|||
Group: Applications/System
|
||||
Requires: %{name}-common = %{version}-%{release}
|
||||
Requires: php-Icinga = %{version}-%{release}
|
||||
%{?amzn:Requires: %{php_cli} >= 5.3.0 bash-completion}
|
||||
%{?fedora:Requires: %{php_cli} >= 5.3.0 bash-completion}
|
||||
%{?rhel:Requires: %{php_cli} >= 5.3.0 bash-completion}
|
||||
%{?suse_version:Requires: %{php} >= 5.3.0}
|
||||
|
@ -167,18 +163,6 @@ Requires: %{php} >= 5.3.0
|
|||
Icinga Web 2 vendor library Parsedown
|
||||
|
||||
|
||||
%package vendor-Zend
|
||||
Version: 1.12.9
|
||||
Release: 1%{?dist}
|
||||
Summary: Icinga Web 2 vendor library Zend Framework
|
||||
Group: Development/Libraries
|
||||
License: BSD
|
||||
Requires: %{php} >= 5.3.0
|
||||
|
||||
%description vendor-Zend
|
||||
Icinga Web 2 vendor library Zend
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
|
@ -186,12 +170,12 @@ Icinga Web 2 vendor library Zend
|
|||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
mkdir -p %{buildroot}/{%{basedir}/{modules,library,public},%{bindir},%{configdir}/modules/setup,%{logdir},%{phpdir},%{wwwconfigdir},%{_sysconfdir}/bash_completion.d,%{docsdir}}
|
||||
mkdir -p %{buildroot}/{%{basedir}/{modules,library/vendor,public},%{bindir},%{configdir}/modules/setup,%{logdir},%{phpdir},%{wwwconfigdir},%{_sysconfdir}/bash_completion.d,%{docsdir}}
|
||||
cp -prv application doc %{buildroot}/%{basedir}
|
||||
cp -pv etc/bash_completion.d/icingacli %{buildroot}/%{_sysconfdir}/bash_completion.d/icingacli
|
||||
cp -prv modules/{monitoring,setup,doc,translation} %{buildroot}/%{basedir}/modules
|
||||
cp -prv library/Icinga %{buildroot}/%{phpdir}
|
||||
cp -prv library/vendor %{buildroot}/%{basedir}/library
|
||||
cp -prv library/vendor/{dompdf,HTMLPurifier,JShrink,lessphp,Parsedown} %{buildroot}/%{basedir}/library/vendor
|
||||
cp -prv public/{css,img,js,error_norewrite.html} %{buildroot}/%{basedir}/public
|
||||
cp -pv packages/files/apache/icingaweb2.conf %{buildroot}/%{wwwconfigdir}/icingaweb2.conf
|
||||
cp -pv packages/files/bin/icingacli %{buildroot}/%{bindir}
|
||||
|
@ -222,12 +206,12 @@ rm -rf %{buildroot}
|
|||
%{basedir}/doc
|
||||
%{basedir}/modules
|
||||
%{basedir}/public
|
||||
%{wwwconfigdir}/icingaweb2.conf
|
||||
%config(noreplace) %{wwwconfigdir}/icingaweb2.conf
|
||||
%attr(2775,root,%{icingawebgroup}) %dir %{logdir}
|
||||
%{docsdir}
|
||||
%docdir %{docsdir}
|
||||
%attr(2770,root,%{icingawebgroup}) %config(noreplace) %dir %{configdir}/modules/setup
|
||||
%attr(0660,root,%{icingawebgroup}) %config(noreplace) %{configdir}/modules/setup/config.ini
|
||||
%{docsdir}
|
||||
%docdir %{docsdir}
|
||||
|
||||
|
||||
%pre common
|
||||
|
@ -277,8 +261,3 @@ exit 0
|
|||
%files vendor-Parsedown
|
||||
%defattr(-,root,root)
|
||||
%{basedir}/library/vendor/Parsedown
|
||||
|
||||
|
||||
%files vendor-Zend
|
||||
%defattr(-,root,root)
|
||||
%{basedir}/library/vendor/Zend
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Icinga\Authentication;
|
|||
use Exception;
|
||||
use Icinga\Authentication\UserGroup\UserGroupBackend;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Application\Logger;
|
||||
|
@ -63,8 +64,11 @@ class Manager
|
|||
);
|
||||
$config = new Config();
|
||||
}
|
||||
if ($config->get('preferences', 'store', 'ini') !== 'none') {
|
||||
$preferencesConfig = $config->getSection('preferences');
|
||||
if ($config->get('global', 'config_backend', 'ini') !== 'none') {
|
||||
$preferencesConfig = new ConfigObject(array(
|
||||
'store' => $config->get('global', 'config_backend', 'ini'),
|
||||
'resource' => $config->get('global', 'config_resource')
|
||||
));
|
||||
try {
|
||||
$preferencesStore = PreferencesStore::create(
|
||||
$preferencesConfig,
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
namespace Icinga\Authentication\User;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Data\Inspectable;
|
||||
use Icinga\Data\Inspection;
|
||||
use PDO;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Icinga\Repository\DbRepository;
|
||||
use Icinga\User;
|
||||
|
||||
class DbUserBackend extends DbRepository implements UserBackendInterface
|
||||
class DbUserBackend extends DbRepository implements UserBackendInterface, Inspectable
|
||||
{
|
||||
/**
|
||||
* The algorithm to use when hashing passwords
|
||||
|
@ -246,4 +248,26 @@ class DbUserBackend extends DbRepository implements UserBackendInterface
|
|||
{
|
||||
return crypt($password, self::HASH_ALGORITHM . ($salt !== null ? $salt : $this->generateSalt()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect this object to gain extended information about its health
|
||||
*
|
||||
* @return Inspection The inspection result
|
||||
*/
|
||||
public function inspect()
|
||||
{
|
||||
$insp = new Inspection('Db User Backend');
|
||||
$insp->write($this->ds->inspect());
|
||||
try {
|
||||
$users = $this->select()->where('is_active', true)->count();
|
||||
if ($users > 1) {
|
||||
$insp->write(sprintf('%s active users', $users));
|
||||
} else {
|
||||
return $insp->error('0 active users', $users);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$insp->error(sprintf('Query failed: %s', $e->getMessage()));
|
||||
}
|
||||
return $insp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace Icinga\Authentication\User;
|
|||
|
||||
use DateTime;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\Inspectable;
|
||||
use Icinga\Data\Inspection;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Repository\LdapRepository;
|
||||
|
@ -13,7 +15,7 @@ use Icinga\Protocol\Ldap\LdapException;
|
|||
use Icinga\Protocol\Ldap\Expression;
|
||||
use Icinga\User;
|
||||
|
||||
class LdapUserBackend extends LdapRepository implements UserBackendInterface
|
||||
class LdapUserBackend extends LdapRepository implements UserBackendInterface, Inspectable
|
||||
{
|
||||
/**
|
||||
* The base DN to use for a query
|
||||
|
@ -218,7 +220,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface
|
|||
throw new ProgrammingError('It is required to set a attribute name where to find a user\'s name first');
|
||||
}
|
||||
|
||||
if ($this->ds->getCapabilities()->hasAdOid()) {
|
||||
if ($this->ds->getCapabilities()->isActiveDirectory()) {
|
||||
$isActiveAttribute = 'userAccountControl';
|
||||
$createdAtAttribute = 'whenCreated';
|
||||
$lastModifiedAttribute = 'whenChanged';
|
||||
|
@ -254,7 +256,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface
|
|||
throw new ProgrammingError('It is required to set the objectClass where to look for users first');
|
||||
}
|
||||
|
||||
if ($this->ds->getCapabilities()->hasAdOid()) {
|
||||
if ($this->ds->getCapabilities()->isActiveDirectory()) {
|
||||
$stateConverter = 'user_account_control';
|
||||
} else {
|
||||
$stateConverter = 'shadow_expire';
|
||||
|
@ -306,41 +308,14 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Probe the backend to test if authentication is possible
|
||||
*
|
||||
* Try to bind to the backend and fetch a single user to check if:
|
||||
* <ul>
|
||||
* <li>Connection credentials are correct and the bind is possible</li>
|
||||
* <li>At least one user exists</li>
|
||||
* <li>The specified userClass has the property specified by userNameAttribute</li>
|
||||
* </ul>
|
||||
|
||||
* @param Inspection $info Optional inspection to fill with diagnostic info
|
||||
*
|
||||
* @throws AuthenticationException When authentication is not possible
|
||||
*/
|
||||
public function assertAuthenticationPossible()
|
||||
public function assertAuthenticationPossible(Inspection $insp = null)
|
||||
{
|
||||
try {
|
||||
$result = $this->select()->fetchRow();
|
||||
} catch (LdapException $e) {
|
||||
throw new AuthenticationException('Connection not possible.', $e);
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
throw new AuthenticationException(
|
||||
'No objects with objectClass "%s" in DN "%s" found. (Filter: %s)',
|
||||
$this->userClass,
|
||||
$this->baseDn ?: $this->ds->getDn(),
|
||||
$this->filter ?: 'None'
|
||||
);
|
||||
}
|
||||
|
||||
if (! isset($result->user_name)) {
|
||||
throw new AuthenticationException(
|
||||
'UserNameAttribute "%s" not existing in objectClass "%s"',
|
||||
$this->userNameAttribute,
|
||||
$this->userClass
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -377,4 +352,58 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect if this LDAP User Backend is working as expected by probing the backend
|
||||
* and testing if thea uthentication is possible
|
||||
*
|
||||
* Try to bind to the backend and fetch a single user to check if:
|
||||
* <ul>
|
||||
* <li>Connection credentials are correct and the bind is possible</li>
|
||||
* <li>At least one user exists</li>
|
||||
* <li>The specified userClass has the property specified by userNameAttribute</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return Inspection Inspection result
|
||||
*/
|
||||
public function inspect()
|
||||
{
|
||||
$result = new Inspection('Ldap User Backend');
|
||||
|
||||
// inspect the used connection to get more diagnostic info in case the connection is not working
|
||||
$result->write($this->ds->inspect());
|
||||
try {
|
||||
try {
|
||||
$res = $this->select()->fetchRow();
|
||||
} catch (LdapException $e) {
|
||||
throw new AuthenticationException('Connection not possible', $e);
|
||||
}
|
||||
$result->write('Searching for: ' . sprintf(
|
||||
'objectClass "%s" in DN "%s" (Filter: %s)',
|
||||
$this->userClass,
|
||||
$this->baseDn ?: $this->ds->getDn(),
|
||||
$this->filter ?: 'None'
|
||||
));
|
||||
if ($res === false) {
|
||||
throw new AuthenticationException('Error, no users found in backend');
|
||||
}
|
||||
$result->write(sprintf('%d users found in backend', $this->select()->count()));
|
||||
if (! isset($res->user_name)) {
|
||||
throw new AuthenticationException(
|
||||
'UserNameAttribute "%s" not existing in objectClass "%s"',
|
||||
$this->userNameAttribute,
|
||||
$this->userClass
|
||||
);
|
||||
}
|
||||
} catch (AuthenticationException $e) {
|
||||
if (($previous = $e->getPrevious()) !== null) {
|
||||
$result->error($previous->getMessage());
|
||||
} else {
|
||||
$result->error($e->getMessage());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$result->error(sprintf('Unable to validate authentication: %s', $e->getMessage()));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,12 +229,12 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
|
|||
*/
|
||||
protected function persistGroupId($groupName)
|
||||
{
|
||||
if (! $groupName || empty($groupName) || is_int($groupName)) {
|
||||
if (! $groupName || empty($groupName) || is_numeric($groupName)) {
|
||||
return $groupName;
|
||||
}
|
||||
|
||||
if (is_array($groupName)) {
|
||||
if (is_int($groupName[0])) {
|
||||
if (is_numeric($groupName[0])) {
|
||||
return $groupName; // In case the array contains mixed types...
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
namespace Icinga\Data\Db;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Data\Inspectable;
|
||||
use Icinga\Data\Inspection;
|
||||
use PDO;
|
||||
use Iterator;
|
||||
use Zend_Db;
|
||||
|
@ -23,7 +26,7 @@ use Icinga\Exception\ProgrammingError;
|
|||
/**
|
||||
* Encapsulate database connections and query creation
|
||||
*/
|
||||
class DbConnection implements Selectable, Extensible, Updatable, Reducible
|
||||
class DbConnection implements Selectable, Extensible, Updatable, Reducible, Inspectable
|
||||
{
|
||||
/**
|
||||
* Connection config
|
||||
|
@ -435,4 +438,42 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible
|
|||
return $column . ' ' . $sign . ' ' . $this->dbAdapter->quote($value);
|
||||
}
|
||||
}
|
||||
|
||||
public function inspect()
|
||||
{
|
||||
$insp = new Inspection('Db Connection');
|
||||
try {
|
||||
$this->getDbAdapter()->getConnection();
|
||||
$config = $this->dbAdapter->getConfig();
|
||||
$insp->write(sprintf(
|
||||
'Connection to %s as %s on %s:%s successful',
|
||||
$config['dbname'],
|
||||
$config['username'],
|
||||
$config['host'],
|
||||
$config['port']
|
||||
));
|
||||
switch ($this->dbType) {
|
||||
case 'mysql':
|
||||
$rows = $this->dbAdapter->query(
|
||||
'SHOW VARIABLES WHERE variable_name ' .
|
||||
'IN (\'version\', \'protocol_version\', \'version_compile_os\');'
|
||||
)->fetchAll();
|
||||
$sqlinsp = new Inspection('MySQL');
|
||||
foreach ($rows as $row) {
|
||||
$sqlinsp->write($row->variable_name . ': ' . $row->value);
|
||||
}
|
||||
$insp->write($sqlinsp);
|
||||
break;
|
||||
case 'pgsql':
|
||||
$row = $this->dbAdapter->query('SELECT version();')->fetchAll();
|
||||
$sqlinsp = new Inspection('PostgreSQL');
|
||||
$sqlinsp->write($row[0]->version);
|
||||
$insp->write($sqlinsp);
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return $insp->error(sprintf('Connection failed %s', $e->getMessage()));
|
||||
}
|
||||
return $insp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Data;
|
||||
|
||||
/**
|
||||
* An object for which the user can retrieve status information
|
||||
*
|
||||
* This interface is useful for providing summaries or diagnostic information about objects
|
||||
* to users.
|
||||
*/
|
||||
interface Inspectable
|
||||
{
|
||||
/**
|
||||
* Inspect this object to gain extended information about its health
|
||||
*
|
||||
* @return Inspection The inspection result
|
||||
*/
|
||||
public function inspect();
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Data;
|
||||
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Contains information about an object in the form of human-readable log entries and indicates if the object has errors
|
||||
*/
|
||||
class Inspection
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $log = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* @var string|Inspection
|
||||
*/
|
||||
protected $error;
|
||||
|
||||
/**
|
||||
* @param $description Describes the object that is being inspected
|
||||
*/
|
||||
public function __construct($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this Inspection
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given log entry or nested inspection
|
||||
*
|
||||
* @throws ProgrammingError When called after erroring
|
||||
*
|
||||
* @param $entry string|Inspection A log entry or nested inspection
|
||||
*/
|
||||
public function write($entry)
|
||||
{
|
||||
if (isset($this->error)) {
|
||||
throw new ProgrammingError('Inspection object used after error');
|
||||
}
|
||||
if ($entry instanceof Inspection) {
|
||||
$this->log[$entry->description] = $entry->toArray();
|
||||
} else {
|
||||
$this->log[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given log entry and fail this inspection with the given error
|
||||
*
|
||||
* @param $entry string|Inspection A log entry or nested inspection
|
||||
*
|
||||
* @throws ProgrammingError When called multiple times
|
||||
*
|
||||
* @return this fluent interface
|
||||
*/
|
||||
public function error($entry)
|
||||
{
|
||||
if (isset($this->error)) {
|
||||
throw new ProgrammingError('Inspection object used after error');
|
||||
}
|
||||
$this->write($entry);
|
||||
$this->error = $entry;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the inspection resulted in an error
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasError()
|
||||
{
|
||||
return isset($this->error);
|
||||
}
|
||||
|
||||
/**
|
||||
* The error that caused the inspection to fail
|
||||
*
|
||||
* @return Inspection|string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the inspection to an array
|
||||
*
|
||||
* @return array An array of strings that describe the state in a human-readable form, each array element
|
||||
* represents one log entry about this object.
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a text representation of the inspection log entries
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf(
|
||||
'Inspection: description: "%s" error: "%s"',
|
||||
$this->description,
|
||||
$this->error
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,13 +13,6 @@ class Discovery {
|
|||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* If discovery was already performed
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $discovered = false;
|
||||
|
||||
/**
|
||||
* @param LdapConnection $conn The ldap connection to use for the discovery
|
||||
*/
|
||||
|
@ -28,17 +21,6 @@ class Discovery {
|
|||
$this->connection = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the discovery on the underlying connection
|
||||
*/
|
||||
private function execDiscovery()
|
||||
{
|
||||
if (! $this->discovered) {
|
||||
$this->connection->connect();
|
||||
$this->discovered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests a resource configuration of hostname, port and root_dn
|
||||
* based on the discovery
|
||||
|
@ -47,10 +29,6 @@ class Discovery {
|
|||
*/
|
||||
public function suggestResourceSettings()
|
||||
{
|
||||
if (! $this->discovered) {
|
||||
$this->execDiscovery();
|
||||
}
|
||||
|
||||
return array(
|
||||
'hostname' => $this->connection->getHostname(),
|
||||
'port' => $this->connection->getPort(),
|
||||
|
@ -66,7 +44,6 @@ class Discovery {
|
|||
*/
|
||||
public function suggestBackendSettings()
|
||||
{
|
||||
$this->execDiscovery();
|
||||
if ($this->isAd()) {
|
||||
return array(
|
||||
'base_dn' => $this->connection->getCapabilities()->getDefaultNamingContext(),
|
||||
|
@ -89,8 +66,7 @@ class Discovery {
|
|||
*/
|
||||
public function isAd()
|
||||
{
|
||||
$this->execDiscovery();
|
||||
return $this->connection->getCapabilities()->hasAdOid();
|
||||
return $this->connection->getCapabilities()->isActiveDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +76,6 @@ class Discovery {
|
|||
*/
|
||||
public function isSuccess()
|
||||
{
|
||||
$this->execDiscovery();
|
||||
return $this->connection->discoverySuccessful();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,8 @@ namespace Icinga\Protocol\Ldap;
|
|||
* Provides information about the available encryption mechanisms (StartTLS), the supported
|
||||
* LDAP protocol (v2/v3), vendor-specific extensions or protocols controls and extensions.
|
||||
*/
|
||||
class Capability
|
||||
class LdapCapabilities
|
||||
{
|
||||
|
||||
const LDAP_SERVER_START_TLS_OID = '1.3.6.1.4.1.1466.20037';
|
||||
|
||||
const LDAP_PAGED_RESULT_OID_STRING = '1.2.840.113556.1.4.319';
|
||||
|
@ -127,7 +126,7 @@ class Capability
|
|||
}
|
||||
|
||||
/**
|
||||
* Return if the capability object contains support for StartTLS
|
||||
* Return if the capability object contains support for paged results
|
||||
*
|
||||
* @return bool Whether StartTLS is supported
|
||||
*/
|
||||
|
@ -141,11 +140,22 @@ class Capability
|
|||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasAdOid()
|
||||
public function isActiveDirectory()
|
||||
{
|
||||
return isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_OID]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the ldap server is an OpenLDAP server
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOpenLdap()
|
||||
{
|
||||
return isset($this->attributes->structuralObjectClass) &&
|
||||
$this->attributes->structuralObjectClass === 'OpenLDAProotDSE';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the capability objects contains support for LdapV3, defaults to true if discovery failed
|
||||
*
|
||||
|
@ -208,4 +218,121 @@ class Capability
|
|||
}
|
||||
return$this->attributes->namingContexts;
|
||||
}
|
||||
|
||||
public function getVendor()
|
||||
{
|
||||
/*
|
||||
rfc #3045 specifies that the name of the server MAY be included in the attribute 'verndorName',
|
||||
AD and OpenLDAP don't do this, but for all all other vendors we follow the standard and
|
||||
just hope for the best.
|
||||
*/
|
||||
|
||||
if ($this->isActiveDirectory()) {
|
||||
return 'Microsoft Active Directory';
|
||||
}
|
||||
|
||||
if ($this->isOpenLdap()) {
|
||||
return 'OpenLDAP';
|
||||
}
|
||||
|
||||
if (! isset($this->attributes->vendorName)) {
|
||||
return null;
|
||||
}
|
||||
return $this->attributes->vendorName;
|
||||
}
|
||||
|
||||
public function getVersion()
|
||||
{
|
||||
/*
|
||||
rfc #3045 specifies that the version of the server MAY be included in the attribute 'vendorVersion',
|
||||
but AD and OpenLDAP don't do this. For OpenLDAP there is no way to query the server versions, but for all
|
||||
all other vendors we follow the standard and just hope for the best.
|
||||
*/
|
||||
|
||||
if ($this->isActiveDirectory()) {
|
||||
return $this->getAdObjectVersionName();
|
||||
}
|
||||
|
||||
if (! isset($this->attributes->vendorVersion)) {
|
||||
return null;
|
||||
}
|
||||
return $this->attributes->vendorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover the capabilities of the given LDAP server
|
||||
*
|
||||
* @param LdapConnection $connection The ldap connection to use
|
||||
*
|
||||
* @return LdapCapabilities
|
||||
*
|
||||
* @throws LdapException In case the capability query has failed
|
||||
*/
|
||||
public static function discoverCapabilities(LdapConnection $connection)
|
||||
{
|
||||
$ds = $connection->getConnection();
|
||||
|
||||
$fields = array(
|
||||
'defaultNamingContext',
|
||||
'namingContexts',
|
||||
'vendorName',
|
||||
'vendorVersion',
|
||||
'supportedSaslMechanisms',
|
||||
'dnsHostName',
|
||||
'schemaNamingContext',
|
||||
'supportedLDAPVersion', // => array(3, 2)
|
||||
'supportedCapabilities',
|
||||
'supportedControl',
|
||||
'supportedExtension',
|
||||
'objectVersion',
|
||||
'+'
|
||||
);
|
||||
|
||||
$result = @ldap_read($ds, '', (string) $connection->select()->from('*', $fields), $fields);
|
||||
if (! $result) {
|
||||
throw new LdapException(
|
||||
'Capability query failed (%s:%d): %s. Check if hostname and port of the'
|
||||
. ' ldap resource are correct and if anonymous access is permitted.',
|
||||
$connection->getHostname(),
|
||||
$connection->getPort(),
|
||||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
|
||||
$entry = ldap_first_entry($ds, $result);
|
||||
if ($entry === false) {
|
||||
throw new LdapException(
|
||||
'Capabilities not available (%s:%d): %s. Discovery of root DSE probably not permitted.',
|
||||
$connection->getHostname(),
|
||||
$connection->getPort(),
|
||||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
$cap = new LdapCapabilities(
|
||||
$connection->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
)
|
||||
);
|
||||
return $cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the active directory version using the available capabillities
|
||||
*
|
||||
* @return null|string The server version description or null when unknown
|
||||
*/
|
||||
protected function getAdObjectVersionName()
|
||||
{
|
||||
if (isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_W8_OID])) {
|
||||
return 'Windows Server 2012 (or newer)';
|
||||
}
|
||||
if (isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_V61_R2_OID])) {
|
||||
return 'Windows Server 2008 R2 (or newer)';
|
||||
}
|
||||
if (isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_V60_OID])) {
|
||||
return 'Windows Server 2008 (or newer)';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -3,20 +3,24 @@
|
|||
|
||||
namespace Icinga\Protocol\Ldap;
|
||||
|
||||
use Exception;
|
||||
use ArrayIterator;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\Inspectable;
|
||||
use Icinga\Data\Inspection;
|
||||
use Icinga\Data\Selectable;
|
||||
use Icinga\Data\Sortable;
|
||||
use Icinga\Exception\InspectionException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Protocol\Ldap\LdapException;
|
||||
|
||||
/**
|
||||
* Encapsulate LDAP connections and query creation
|
||||
*/
|
||||
class LdapConnection implements Selectable
|
||||
class LdapConnection implements Selectable, Inspectable
|
||||
{
|
||||
/**
|
||||
* Indicates that the target object cannot be found
|
||||
|
@ -142,17 +146,24 @@ class LdapConnection implements Selectable
|
|||
/**
|
||||
* The properties and capabilities of the LDAP server
|
||||
*
|
||||
* @var Capability
|
||||
* @var LdapCapabilities
|
||||
*/
|
||||
protected $capabilities;
|
||||
|
||||
/**
|
||||
* Whether discovery was successful or not
|
||||
* Whether discovery was successful
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $discoverySuccess;
|
||||
|
||||
/**
|
||||
* Whether the current connection is encrypted
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $encrypted = null;
|
||||
|
||||
/**
|
||||
* Create a new connection object
|
||||
*
|
||||
|
@ -217,46 +228,88 @@ class LdapConnection implements Selectable
|
|||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the LDAP link identifier being used
|
||||
*
|
||||
* Establishes a connection if necessary.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
if ($this->ds === null) {
|
||||
$this->ds = $this->prepareNewConnection();
|
||||
}
|
||||
|
||||
return $this->ds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the capabilities of the current connection
|
||||
*
|
||||
* @return Capability
|
||||
* @return LdapCapabilities
|
||||
*/
|
||||
public function getCapabilities()
|
||||
{
|
||||
if ($this->capabilities === null) {
|
||||
$this->connect(); // Populates $this->capabilities
|
||||
try {
|
||||
$this->capabilities = LdapCapabilities::discoverCapabilities($this);
|
||||
$this->discoverySuccess = true;
|
||||
} catch (LdapException $e) {
|
||||
Logger::debug($e);
|
||||
Logger::warning('LADP discovery failed, assuming default LDAP capabilities.');
|
||||
$this->capabilities = new LdapCapabilities(); // create empty default capabilities
|
||||
$this->discoverySuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether discovery was successful or not
|
||||
* Return whether discovery was successful
|
||||
*
|
||||
* @return bool true if the capabilities were successfully determined, false if the capabilities were guessed
|
||||
*/
|
||||
public function discoverySuccessful()
|
||||
{
|
||||
if ($this->discoverySuccess === null) {
|
||||
$this->getCapabilities(); // Initializes self::$discoverySuccess
|
||||
}
|
||||
|
||||
return $this->discoverySuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the current connection is encrypted
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEncrypted()
|
||||
{
|
||||
if ($this->encrypted === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a connection
|
||||
*
|
||||
* @throws LdapException In case the connection could not be established
|
||||
*
|
||||
* @deprecated The connection is established lazily now
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if ($this->ds === null) {
|
||||
$this->ds = $this->prepareNewConnection();
|
||||
}
|
||||
$this->getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a LDAP bind on the current connection
|
||||
*
|
||||
* @throws LdapException In case the LDAP bind was unsuccessful
|
||||
* @throws LdapException In case the LDAP bind was unsuccessful or insecure
|
||||
*/
|
||||
public function bind()
|
||||
{
|
||||
|
@ -264,7 +317,9 @@ class LdapConnection implements Selectable
|
|||
return;
|
||||
}
|
||||
|
||||
$success = @ldap_bind($this->ds, $this->bindDn, $this->bindPw);
|
||||
$ds = $this->getConnection();
|
||||
|
||||
$success = @ldap_bind($ds, $this->bindDn, $this->bindPw);
|
||||
if (! $success) {
|
||||
throw new LdapException(
|
||||
'LDAP connection to %s:%s (%s / %s) failed: %s',
|
||||
|
@ -272,7 +327,7 @@ class LdapConnection implements Selectable
|
|||
$this->port,
|
||||
$this->bindDn,
|
||||
'***' /* $this->bindPw */,
|
||||
ldap_error($this->ds)
|
||||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -310,7 +365,6 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function count(LdapQuery $query)
|
||||
{
|
||||
$this->connect();
|
||||
$this->bind();
|
||||
|
||||
$res = $this->runQuery($query, array());
|
||||
|
@ -327,11 +381,9 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function fetchAll(LdapQuery $query, array $fields = null)
|
||||
{
|
||||
$this->connect();
|
||||
$this->bind();
|
||||
|
||||
if (
|
||||
$query->getUsePagedResults()
|
||||
if ($query->getUsePagedResults()
|
||||
&& version_compare(PHP_VERSION, '5.4.0') >= 0
|
||||
&& $this->getCapabilities()->hasPagedResult()
|
||||
) {
|
||||
|
@ -465,21 +517,20 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function testCredentials($bindDn, $bindPw)
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$success = @ldap_bind($this->ds, $bindDn, $bindPw);
|
||||
$ds = $this->getConnection();
|
||||
$success = @ldap_bind($ds, $bindDn, $bindPw);
|
||||
if (! $success) {
|
||||
if (ldap_errno($this->ds) === self::LDAP_INVALID_CREDENTIALS) {
|
||||
if (ldap_errno($ds) === self::LDAP_INVALID_CREDENTIALS) {
|
||||
Logger::debug(
|
||||
'Testing LDAP credentials (%s / %s) failed: %s',
|
||||
$bindDn,
|
||||
'***',
|
||||
ldap_error($this->ds)
|
||||
ldap_error($ds)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new LdapException(ldap_error($this->ds));
|
||||
throw new LdapException(ldap_error($ds));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -494,11 +545,11 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function hasDn($dn)
|
||||
{
|
||||
$this->connect();
|
||||
$this->bind();
|
||||
|
||||
$result = ldap_read($this->ds, $dn, '(objectClass=*)', array('objectClass'));
|
||||
return ldap_count_entries($this->ds, $result) > 0;
|
||||
$ds = $this->getConnection();
|
||||
$result = ldap_read($ds, $dn, '(objectClass=*)', array('objectClass'));
|
||||
return ldap_count_entries($ds, $result) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -512,19 +563,19 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function deleteRecursively($dn)
|
||||
{
|
||||
$this->connect();
|
||||
$this->bind();
|
||||
|
||||
$result = @ldap_list($this->ds, $dn, '(objectClass=*)', array('objectClass'));
|
||||
$ds = $this->getConnection();
|
||||
$result = @ldap_list($ds, $dn, '(objectClass=*)', array('objectClass'));
|
||||
if ($result === false) {
|
||||
if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new LdapException('LDAP list for "%s" failed: %s', $dn, ldap_error($this->ds));
|
||||
throw new LdapException('LDAP list for "%s" failed: %s', $dn, ldap_error($ds));
|
||||
}
|
||||
|
||||
$children = ldap_get_entries($this->ds, $result);
|
||||
$children = ldap_get_entries($ds, $result);
|
||||
for ($i = 0; $i < $children['count']; $i++) {
|
||||
$result = $this->deleteRecursively($children[$i]['dn']);
|
||||
if (! $result) {
|
||||
|
@ -547,16 +598,16 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function deleteDn($dn)
|
||||
{
|
||||
$this->connect();
|
||||
$this->bind();
|
||||
|
||||
$result = @ldap_delete($this->ds, $dn);
|
||||
$ds = $this->getConnection();
|
||||
$result = @ldap_delete($ds, $dn);
|
||||
if ($result === false) {
|
||||
if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
return false; // TODO: Isn't it a success if something i'd like to remove is not existing at all???
|
||||
}
|
||||
|
||||
throw new LdapException('LDAP delete for "%s" failed: %s', $dn, ldap_error($this->ds));
|
||||
throw new LdapException('LDAP delete for "%s" failed: %s', $dn, ldap_error($ds));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -600,11 +651,13 @@ class LdapConnection implements Selectable
|
|||
$fields = $query->getColumns();
|
||||
}
|
||||
|
||||
$ds = $this->getConnection();
|
||||
|
||||
$serverSorting = false;//$this->capabilities->hasOid(Capability::LDAP_SERVER_SORT_OID);
|
||||
if ($serverSorting && $query->hasOrder()) {
|
||||
ldap_set_option($this->ds, LDAP_OPT_SERVER_CONTROLS, array(
|
||||
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(
|
||||
array(
|
||||
'oid' => Capability::LDAP_SERVER_SORT_OID,
|
||||
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID,
|
||||
'value' => $this->encodeSortRules($query->getOrder())
|
||||
)
|
||||
));
|
||||
|
@ -617,7 +670,7 @@ class LdapConnection implements Selectable
|
|||
}
|
||||
|
||||
$results = @ldap_search(
|
||||
$this->ds,
|
||||
$ds,
|
||||
$query->getBase() ?: $this->rootDn,
|
||||
(string) $query,
|
||||
array_values($fields),
|
||||
|
@ -625,7 +678,7 @@ class LdapConnection implements Selectable
|
|||
$serverSorting && $limit ? $offset + $limit : 0
|
||||
);
|
||||
if ($results === false) {
|
||||
if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
@ -633,25 +686,25 @@ class LdapConnection implements Selectable
|
|||
'LDAP query "%s" (base %s) failed. Error: %s',
|
||||
$query,
|
||||
$query->getBase() ?: $this->rootDn,
|
||||
ldap_error($this->ds)
|
||||
ldap_error($ds)
|
||||
);
|
||||
} elseif (ldap_count_entries($this->ds, $results) === 0) {
|
||||
} elseif (ldap_count_entries($ds, $results) === 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$entries = array();
|
||||
$entry = ldap_first_entry($this->ds, $results);
|
||||
$entry = ldap_first_entry($ds, $results);
|
||||
do {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($this->ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($this->ds, $entry), array_flip($fields)
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
);
|
||||
}
|
||||
} while (
|
||||
(! $serverSorting || $limit === 0 || $limit !== count($entries))
|
||||
&& ($entry = ldap_next_entry($this->ds, $entry))
|
||||
} while ((! $serverSorting || $limit === 0 || $limit !== count($entries))
|
||||
&& ($entry = ldap_next_entry($ds, $entry))
|
||||
);
|
||||
|
||||
if (! $serverSorting && $query->hasOrder()) {
|
||||
|
@ -693,11 +746,13 @@ class LdapConnection implements Selectable
|
|||
$fields = $query->getColumns();
|
||||
}
|
||||
|
||||
$ds = $this->getConnection();
|
||||
|
||||
$serverSorting = false;//$this->capabilities->hasOid(Capability::LDAP_SERVER_SORT_OID);
|
||||
if ($serverSorting && $query->hasOrder()) {
|
||||
ldap_set_option($this->ds, LDAP_OPT_SERVER_CONTROLS, array(
|
||||
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(
|
||||
array(
|
||||
'oid' => Capability::LDAP_SERVER_SORT_OID,
|
||||
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID,
|
||||
'value' => $this->encodeSortRules($query->getOrder())
|
||||
)
|
||||
));
|
||||
|
@ -715,10 +770,10 @@ class LdapConnection implements Selectable
|
|||
do {
|
||||
// Do not request the pagination control as a critical extension, as we want the
|
||||
// server to return results even if the paged search request cannot be satisfied
|
||||
ldap_control_paged_result($this->ds, $pageSize, false, $cookie);
|
||||
ldap_control_paged_result($ds, $pageSize, false, $cookie);
|
||||
|
||||
$results = @ldap_search(
|
||||
$this->ds,
|
||||
$ds,
|
||||
$base,
|
||||
$queryString,
|
||||
array_values($fields),
|
||||
|
@ -726,7 +781,7 @@ class LdapConnection implements Selectable
|
|||
$serverSorting && $limit ? $offset + $limit : 0
|
||||
);
|
||||
if ($results === false) {
|
||||
if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -734,37 +789,38 @@ class LdapConnection implements Selectable
|
|||
'LDAP query "%s" (base %s) failed. Error: %s',
|
||||
$queryString,
|
||||
$base,
|
||||
ldap_error($this->ds)
|
||||
ldap_error($ds)
|
||||
);
|
||||
} elseif (ldap_count_entries($this->ds, $results) === 0) {
|
||||
} elseif (ldap_count_entries($ds, $results) === 0) {
|
||||
if (in_array(
|
||||
ldap_errno($this->ds),
|
||||
ldap_errno($ds),
|
||||
array(static::LDAP_SIZELIMIT_EXCEEDED, static::LDAP_ADMINLIMIT_EXCEEDED)
|
||||
)) {
|
||||
Logger::warning(
|
||||
'Unable to request more than %u results. Does the server allow paged search requests? (%s)',
|
||||
$count,
|
||||
ldap_error($this->ds)
|
||||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$entry = ldap_first_entry($this->ds, $results);
|
||||
$entry = ldap_first_entry($ds, $results);
|
||||
do {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($this->ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($this->ds, $entry), array_flip($fields)
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
);
|
||||
}
|
||||
} while (
|
||||
(! $serverSorting || $limit === 0 || $limit !== count($entries))
|
||||
&& ($entry = ldap_next_entry($this->ds, $entry))
|
||||
&& ($entry = ldap_next_entry($ds, $entry))
|
||||
);
|
||||
|
||||
if (false === @ldap_control_paged_result_response($this->ds, $results, $cookie)) {
|
||||
if (false === @ldap_control_paged_result_response($ds, $results, $cookie)) {
|
||||
// If the page size is greater than or equal to the sizeLimit value, the server should ignore the
|
||||
// control as the request can be satisfied in a single page: https://www.ietf.org/rfc/rfc2696.txt
|
||||
// This applies no matter whether paged search requests are permitted or not. You're done once you
|
||||
|
@ -785,11 +841,11 @@ class LdapConnection implements Selectable
|
|||
// A sequence of paged search requests is abandoned by the client sending a search request containing a
|
||||
// pagedResultsControl with the size set to zero (0) and the cookie set to the last cookie returned by
|
||||
// the server: https://www.ietf.org/rfc/rfc2696.txt
|
||||
ldap_control_paged_result($this->ds, 0, false, $cookie);
|
||||
ldap_search($this->ds, $base, $queryString); // Returns no entries, due to the page size
|
||||
ldap_control_paged_result($ds, 0, false, $cookie);
|
||||
ldap_search($ds, $base, $queryString); // Returns no entries, due to the page size
|
||||
} else {
|
||||
// Reset the paged search request so that subsequent requests succeed
|
||||
ldap_control_paged_result($this->ds, 0);
|
||||
ldap_control_paged_result($ds, 0);
|
||||
}
|
||||
|
||||
if (! $serverSorting && $query->hasOrder()) {
|
||||
|
@ -812,7 +868,7 @@ class LdapConnection implements Selectable
|
|||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function cleanupAttributes($attributes, array $requestedFields)
|
||||
public function cleanupAttributes($attributes, array $requestedFields)
|
||||
{
|
||||
// In case the result contains attributes with a differing case than the requested fields, it is
|
||||
// necessary to create another array to map attributes case insensitively to their requested counterparts.
|
||||
|
@ -862,6 +918,7 @@ class LdapConnection implements Selectable
|
|||
* @param array $sortRules
|
||||
*
|
||||
* @return string
|
||||
* @throws ProgrammingError
|
||||
*
|
||||
* @todo Produces an invalid stream, obviously
|
||||
*/
|
||||
|
@ -887,63 +944,56 @@ class LdapConnection implements Selectable
|
|||
/**
|
||||
* Prepare and establish a connection with the LDAP server
|
||||
*
|
||||
* @return resource A positive LDAP link identifier
|
||||
* @param Inspection $info Optional inspection to fill with diagnostic info
|
||||
*
|
||||
* @throws LdapException In case the connection is not possible
|
||||
* @return resource A LDAP link identifier
|
||||
*
|
||||
* @throws LdapException In case the connection is not possible
|
||||
*/
|
||||
protected function prepareNewConnection()
|
||||
protected function prepareNewConnection(Inspection $info = null)
|
||||
{
|
||||
if (! isset($info)) {
|
||||
$info = new Inspection('');
|
||||
}
|
||||
|
||||
if ($this->encryption === static::STARTTLS || $this->encryption === static::LDAPS) {
|
||||
$this->prepareTlsEnvironment();
|
||||
}
|
||||
|
||||
$hostname = $this->hostname;
|
||||
if ($this->encryption === static::LDAPS) {
|
||||
$info->write('Connect using LDAPS');
|
||||
if (! $this->validateCertificate) {
|
||||
$info->write('Skip certificate validation');
|
||||
}
|
||||
$hostname = 'ldaps://' . $hostname;
|
||||
}
|
||||
|
||||
$ds = ldap_connect($hostname, $this->port);
|
||||
|
||||
try {
|
||||
$this->capabilities = $this->discoverCapabilities($ds);
|
||||
$this->discoverySuccess = true;
|
||||
} catch (LdapException $e) {
|
||||
Logger::debug($e);
|
||||
Logger::warning('LADP discovery failed, assuming default LDAP capabilities.');
|
||||
$this->capabilities = new Capability(); // create empty default capabilities
|
||||
$this->discoverySuccess = false;
|
||||
}
|
||||
|
||||
if ($this->encryption === static::STARTTLS) {
|
||||
$force_tls = false;
|
||||
if ($this->capabilities->hasStartTls()) {
|
||||
if (@ldap_start_tls($ds)) {
|
||||
Logger::debug('LDAP STARTTLS succeeded');
|
||||
} else {
|
||||
Logger::error('LDAP STARTTLS failed: %s', ldap_error($ds));
|
||||
throw new LdapException('LDAP STARTTLS failed: %s', ldap_error($ds));
|
||||
}
|
||||
} elseif ($force_tls) {
|
||||
throw new LdapException('STARTTLS is required but not announced by %s', $this->hostname);
|
||||
} else {
|
||||
Logger::warning('LDAP STARTTLS enabled but not announced');
|
||||
}
|
||||
}
|
||||
|
||||
// ldap_rename requires LDAPv3:
|
||||
if ($this->capabilities->hasLdapV3()) {
|
||||
if (! ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
|
||||
throw new LdapException('LDAPv3 is required');
|
||||
}
|
||||
} else {
|
||||
// TODO: remove this -> FORCING v3 for now
|
||||
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
Logger::warning('No LDAPv3 support detected');
|
||||
}
|
||||
// Usage of ldap_rename, setting LDAP_OPT_REFERRALS to 0 or using STARTTLS requires LDAPv3.
|
||||
// If this does not work we're probably not in a PHP 5.3+ environment as it is VERY
|
||||
// unlikely that the server complains about it by itself prior to a bind request
|
||||
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
|
||||
// Not setting this results in "Operations error" on AD when using the whole domain as search base
|
||||
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
|
||||
// ldap_set_option($ds, LDAP_OPT_DEREF, LDAP_DEREF_NEVER);
|
||||
|
||||
if ($this->encryption === static::STARTTLS) {
|
||||
$this->encrypted = true;
|
||||
$info->write('Connect using STARTTLS');
|
||||
if (! $this->validateCertificate) {
|
||||
$info->write('Skip certificate validation');
|
||||
}
|
||||
if (! ldap_start_tls($ds)) {
|
||||
throw new LdapException('LDAP STARTTLS failed: %s', ldap_error($ds));
|
||||
}
|
||||
|
||||
} elseif ($this->encryption !== static::LDAPS) {
|
||||
$this->encrypted = false;
|
||||
$info->write('Connect without encryption');
|
||||
}
|
||||
|
||||
return $ds;
|
||||
}
|
||||
|
||||
|
@ -971,56 +1021,6 @@ class LdapConnection implements Selectable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover the capabilities of the given LDAP server
|
||||
*
|
||||
* @param resource $ds The link identifier of the current LDAP connection
|
||||
*
|
||||
* @return Capability
|
||||
*
|
||||
* @throws LdapException In case the capability query has failed
|
||||
*/
|
||||
protected function discoverCapabilities($ds)
|
||||
{
|
||||
$fields = array(
|
||||
'defaultNamingContext',
|
||||
'namingContexts',
|
||||
'vendorName',
|
||||
'vendorVersion',
|
||||
'supportedSaslMechanisms',
|
||||
'dnsHostName',
|
||||
'schemaNamingContext',
|
||||
'supportedLDAPVersion', // => array(3, 2)
|
||||
'supportedCapabilities',
|
||||
'supportedControl',
|
||||
'supportedExtension',
|
||||
'+'
|
||||
);
|
||||
|
||||
$result = @ldap_read($ds, '', (string) $this->select()->from('*', $fields), $fields);
|
||||
if (! $result) {
|
||||
throw new LdapException(
|
||||
'Capability query failed (%s:%d): %s. Check if hostname and port of the'
|
||||
. ' ldap resource are correct and if anonymous access is permitted.',
|
||||
$this->hostname,
|
||||
$this->port,
|
||||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
|
||||
$entry = ldap_first_entry($ds, $result);
|
||||
if ($entry === false) {
|
||||
throw new LdapException(
|
||||
'Capabilities not available (%s:%d): %s. Discovery of root DSE probably not permitted.',
|
||||
$this->hostname,
|
||||
$this->port,
|
||||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
|
||||
return new Capability($this->cleanupAttributes(ldap_get_attributes($ds, $entry), array_flip($fields)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an LDAP entry
|
||||
*
|
||||
|
@ -1031,7 +1031,7 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function addEntry($dn, array $attributes)
|
||||
{
|
||||
return ldap_add($this->ds, $dn, $attributes);
|
||||
return ldap_add($this->getConnection(), $dn, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1044,7 +1044,7 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function modifyEntry($dn, array $attributes)
|
||||
{
|
||||
return ldap_modify($this->ds, $dn, $attributes);
|
||||
return ldap_modify($this->getConnection(), $dn, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1060,9 +1060,10 @@ class LdapConnection implements Selectable
|
|||
*/
|
||||
public function moveEntry($dn, $newRdn, $newParentDn)
|
||||
{
|
||||
$result = ldap_rename($this->ds, $dn, $newRdn, $newParentDn, false);
|
||||
$ds = $this->getConnection();
|
||||
$result = ldap_rename($ds, $dn, $newRdn, $newParentDn, false);
|
||||
if ($result === false) {
|
||||
throw new LdapException('Could not move entry "%s" to "%s": %s', $dn, $newRdn, ldap_error($this->ds));
|
||||
throw new LdapException('Could not move entry "%s" to "%s": %s', $dn, $newRdn, ldap_error($ds));
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
@ -1085,6 +1086,57 @@ class LdapConnection implements Selectable
|
|||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect if this LDAP Connection is working as expected
|
||||
*
|
||||
* Check if connection, bind and encryption is working as expected and get additional
|
||||
* information about the used
|
||||
*
|
||||
* @return Inspection Inspection result
|
||||
*/
|
||||
public function inspect()
|
||||
{
|
||||
$insp = new Inspection('Ldap Connection');
|
||||
|
||||
// Try to connect to the server with the given connection parameters
|
||||
try {
|
||||
$ds = $this->prepareNewConnection($insp);
|
||||
} catch (Exception $e) {
|
||||
return $insp->error($e->getMessage());
|
||||
}
|
||||
|
||||
// Try a bind-command with the given user credentials, this must not fail
|
||||
$success = @ldap_bind($ds, $this->bindDn, $this->bindPw);
|
||||
$msg = sprintf(
|
||||
'LDAP bind to %s:%s (%s / %s)',
|
||||
$this->hostname,
|
||||
$this->port,
|
||||
$this->bindDn,
|
||||
'***' /* $this->bindPw */
|
||||
);
|
||||
if (! $success) {
|
||||
return $insp->error(sprintf('%s failed: %s', $msg, ldap_error($ds)));
|
||||
}
|
||||
$insp->write(sprintf($msg . ' successful'));
|
||||
|
||||
// Try to execute a schema discovery this may fail if schema discovery is not supported
|
||||
try {
|
||||
$cap = LdapCapabilities::discoverCapabilities($this);
|
||||
$discovery = new Inspection('Discovery Results');
|
||||
$discovery->write($cap->getVendor());
|
||||
$version = $cap->getVersion();
|
||||
if (isset($version)) {
|
||||
$discovery->write($version);
|
||||
}
|
||||
$discovery->write('Supports STARTTLS: ' . ($cap->hasStartTls() ? 'True' : 'False'));
|
||||
$discovery->write('Default naming context: ' . $cap->getDefaultNamingContext());
|
||||
$insp->write($discovery);
|
||||
} catch (Exception $e) {
|
||||
$insp->write('Schema discovery not possible: ' . $e->getMessage());
|
||||
}
|
||||
return $insp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the environment variables set by self::prepareTlsEnvironment()
|
||||
*/
|
||||
|
|
|
@ -497,19 +497,24 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
/**
|
||||
* Return the name of the conversion method for the given alias or column name and context
|
||||
*
|
||||
* @param array|string $table The datasource's table
|
||||
* @param string $name The alias or column name for which to return a conversion method
|
||||
* @param string $context The context of the conversion: persist or retrieve
|
||||
* If a query column or a filter column, which is part of a query filter, needs to be converted,
|
||||
* you'll need to pass $query, otherwise the column is considered a statement column.
|
||||
*
|
||||
* @param string $table The datasource's table
|
||||
* @param string $name The alias or column name for which to return a conversion method
|
||||
* @param string $context The context of the conversion: persist or retrieve
|
||||
* @param RepositoryQuery $query If given the column is considered a query column,
|
||||
* statement column otherwise
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws ProgrammingError In case a conversion rule is found but not any conversion method
|
||||
*/
|
||||
protected function getConverter($table, $name, $context)
|
||||
protected function getConverter($table, $name, $context, RepositoryQuery $query = null)
|
||||
{
|
||||
if (
|
||||
$this->validateQueryColumnAssociation($table, $name)
|
||||
|| $this->validateStatementColumnAssociation($table, $name)
|
||||
($query !== null && $this->validateQueryColumnAssociation($table, $name))
|
||||
|| ($query === null && $this->validateStatementColumnAssociation($table, $name))
|
||||
) {
|
||||
$table = $this->removeTablePrefix($this->clearTableAlias($table));
|
||||
} else {
|
||||
|
@ -519,7 +524,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
}
|
||||
}
|
||||
|
||||
return parent::getConverter($table, $name, $context);
|
||||
return parent::getConverter($table, $name, $context, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -650,7 +655,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
* Attempts to join the given column from a different table if its association to the given table cannot be
|
||||
* verified.
|
||||
*
|
||||
* @param string $table The table where to look for the column or alias
|
||||
* @param array|string $table The table where to look for the column or alias
|
||||
* @param string $name The name or alias of the column to validate
|
||||
* @param RepositoryQuery $query An optional query to pass as context,
|
||||
* if not given no join will be attempted
|
||||
|
@ -662,7 +667,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
public function requireQueryColumn($table, $name, RepositoryQuery $query = null)
|
||||
{
|
||||
if ($query === null || $this->validateQueryColumnAssociation($table, $name)) {
|
||||
return parent::requireQueryColumn($table, $name, $query);
|
||||
return parent::requireQueryColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name, $query);
|
||||
}
|
||||
|
||||
return $this->joinColumn($name, $table, $query);
|
||||
|
@ -674,7 +679,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
* Attempts to join the given column from a different table if its association to the given table cannot be
|
||||
* verified.
|
||||
*
|
||||
* @param string $table The table where to look for the column or alias
|
||||
* @param array|string $table The table where to look for the column or alias
|
||||
* @param string $name The name or alias of the column to validate
|
||||
* @param RepositoryQuery $query An optional query to pass as context,
|
||||
* if not given the column is considered being used for a statement filter
|
||||
|
@ -690,7 +695,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
}
|
||||
|
||||
if ($this->validateQueryColumnAssociation($table, $name)) {
|
||||
return parent::requireFilterColumn($table, $name, $query);
|
||||
return parent::requireFilterColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name, $query);
|
||||
}
|
||||
|
||||
return $this->joinColumn($name, $table, $query);
|
||||
|
@ -699,8 +704,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
/**
|
||||
* Return the statement column name for the given alias or null in case the alias does not exist
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $alias
|
||||
* @param array|string $table
|
||||
* @param string $alias
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
|
@ -711,7 +716,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
return $statementAliasColumnMap[$alias];
|
||||
}
|
||||
|
||||
$prefixedAlias = $this->removeTablePrefix($table) . '.' . $alias;
|
||||
$prefixedAlias = $this->removeTablePrefix($this->clearTableAlias($table)) . '.' . $alias;
|
||||
if (isset($statementAliasColumnMap[$prefixedAlias])) {
|
||||
return $statementAliasColumnMap[$prefixedAlias];
|
||||
}
|
||||
|
@ -720,8 +725,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
/**
|
||||
* Return the alias for the given statement column name or null in case the statement column does not exist
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @param array|string $table
|
||||
* @param string $column
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
|
@ -732,7 +737,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
return $statementColumnAliasMap[$column];
|
||||
}
|
||||
|
||||
$prefixedColumn = $this->removeTablePrefix($table) . '.' . $column;
|
||||
$prefixedColumn = $this->removeTablePrefix($this->clearTableAlias($table)) . '.' . $column;
|
||||
if (isset($statementColumnAliasMap[$prefixedColumn])) {
|
||||
return $statementColumnAliasMap[$prefixedColumn];
|
||||
}
|
||||
|
@ -741,14 +746,14 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
/**
|
||||
* Return whether the given alias or statement column name is available in the given table
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $alias
|
||||
* @param array|string $table
|
||||
* @param string $alias
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateStatementColumnAssociation($table, $alias)
|
||||
{
|
||||
$tableName = $this->removeTablePrefix($table);
|
||||
$tableName = $this->removeTablePrefix($this->clearTableAlias($table));
|
||||
|
||||
$statementAliasTableMap = $this->getStatementAliasTableMap();
|
||||
if (isset($statementAliasTableMap[$alias])) {
|
||||
|
@ -767,8 +772,8 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
/**
|
||||
* Return whether the given column name or alias of the given table is a valid statement column
|
||||
*
|
||||
* @param string $table The table where to look for the column or alias
|
||||
* @param string $name The column name or alias to check
|
||||
* @param array|string $table The table where to look for the column or alias
|
||||
* @param string $name The column name or alias to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -779,7 +784,7 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
&& $this->reassembleStatementColumnAlias($table, $name) === null)
|
||||
|| !$this->validateStatementColumnAssociation($table, $name)
|
||||
) {
|
||||
return parent::hasStatementColumn($table, $name);
|
||||
return parent::hasStatementColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -788,12 +793,12 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
/**
|
||||
* Validate that the given column is a valid statement column and return it or the actual name if it's an alias
|
||||
*
|
||||
* @param string $table The table for which to require the column
|
||||
* @param string $name The name or alias of the column to validate
|
||||
* @param array|string $table The table for which to require the column
|
||||
* @param string $name The name or alias of the column to validate
|
||||
*
|
||||
* @return string The given column's name
|
||||
* @return string The given column's name
|
||||
*
|
||||
* @throws StatementException In case the given column is not a statement column
|
||||
* @throws StatementException In case the given column is not a statement column
|
||||
*/
|
||||
public function requireStatementColumn($table, $name)
|
||||
{
|
||||
|
@ -802,11 +807,15 @@ abstract class DbRepository extends Repository implements Extensible, Updatable,
|
|||
} elseif (($alias = $this->reassembleStatementColumnAlias($table, $name)) !== null) {
|
||||
$column = $name;
|
||||
} else {
|
||||
return parent::requireStatementColumn($table, $name);
|
||||
return parent::requireStatementColumn($this->removeTablePrefix($this->clearTableAlias($table)), $name);
|
||||
}
|
||||
|
||||
if (! $this->validateStatementColumnAssociation($table, $alias)) {
|
||||
throw new StatementException('Statement column "%s" not found in table "%s"', $name, $table);
|
||||
throw new StatementException(
|
||||
'Statement column "%s" not found in table "%s"',
|
||||
$name,
|
||||
$this->removeTablePrefix($this->clearTableAlias($table))
|
||||
);
|
||||
}
|
||||
|
||||
return $column;
|
||||
|
|
|
@ -491,15 +491,18 @@ abstract class Repository implements Selectable
|
|||
/**
|
||||
* Convert a value supposed to be transmitted to the data source
|
||||
*
|
||||
* @param string $table The table where to persist the value
|
||||
* @param string $name The alias or column name
|
||||
* @param mixed $value The value to convert
|
||||
* @param string $table The table where to persist the value
|
||||
* @param string $name The alias or column name
|
||||
* @param mixed $value The value to convert
|
||||
* @param RepositoryQuery $query An optional query to pass as context
|
||||
* (Directly passed through to $this->getConverter)
|
||||
*
|
||||
* @return mixed If conversion was possible, the converted value, otherwise the unchanged value
|
||||
* @return mixed If conversion was possible, the converted value,
|
||||
* otherwise the unchanged value
|
||||
*/
|
||||
public function persistColumn($table, $name, $value)
|
||||
public function persistColumn($table, $name, $value, RepositoryQuery $query = null)
|
||||
{
|
||||
$converter = $this->getConverter($table, $name, 'persist');
|
||||
$converter = $this->getConverter($table, $name, 'persist', $query);
|
||||
if ($converter !== null) {
|
||||
$value = $this->$converter($value);
|
||||
}
|
||||
|
@ -510,15 +513,18 @@ abstract class Repository implements Selectable
|
|||
/**
|
||||
* Convert a value which was fetched from the data source
|
||||
*
|
||||
* @param string $table The table the value has been fetched from
|
||||
* @param string $name The alias or column name
|
||||
* @param mixed $value The value to convert
|
||||
* @param string $table The table the value has been fetched from
|
||||
* @param string $name The alias or column name
|
||||
* @param mixed $value The value to convert
|
||||
* @param RepositoryQuery $query An optional query to pass as context
|
||||
* (Directly passed through to $this->getConverter)
|
||||
*
|
||||
* @return mixed If conversion was possible, the converted value, otherwise the unchanged value
|
||||
* @return mixed If conversion was possible, the converted value,
|
||||
* otherwise the unchanged value
|
||||
*/
|
||||
public function retrieveColumn($table, $name, $value)
|
||||
public function retrieveColumn($table, $name, $value, RepositoryQuery $query = null)
|
||||
{
|
||||
$converter = $this->getConverter($table, $name, 'retrieve');
|
||||
$converter = $this->getConverter($table, $name, 'retrieve', $query);
|
||||
if ($converter !== null) {
|
||||
$value = $this->$converter($value);
|
||||
}
|
||||
|
@ -529,15 +535,17 @@ abstract class Repository implements Selectable
|
|||
/**
|
||||
* Return the name of the conversion method for the given alias or column name and context
|
||||
*
|
||||
* @param string $table The datasource's table
|
||||
* @param string $name The alias or column name for which to return a conversion method
|
||||
* @param string $context The context of the conversion: persist or retrieve
|
||||
* @param string $table The datasource's table
|
||||
* @param string $name The alias or column name for which to return a conversion method
|
||||
* @param string $context The context of the conversion: persist or retrieve
|
||||
* @param RepositoryQuery $query An optional query to pass as context
|
||||
* (unused by the base implementation)
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws ProgrammingError In case a conversion rule is found but not any conversion method
|
||||
*/
|
||||
protected function getConverter($table, $name, $context)
|
||||
protected function getConverter($table, $name, $context, RepositoryQuery $query = null)
|
||||
{
|
||||
$conversionRules = $this->getConversionRules();
|
||||
if (! isset($conversionRules[$table])) {
|
||||
|
|
|
@ -153,7 +153,7 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
{
|
||||
$this->query->where(
|
||||
$this->repository->requireFilterColumn($this->target, $column, $this),
|
||||
$this->repository->persistColumn($this->target, $column, $value)
|
||||
$this->repository->persistColumn($this->target, $column, $value, $this)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
@ -401,7 +401,7 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
if ($result !== false && $this->repository->providesValueConversion($this->target)) {
|
||||
$columns = $this->getColumns();
|
||||
$column = isset($columns[0]) ? $columns[0] : key($columns);
|
||||
return $this->repository->retrieveColumn($this->target, $column, $result);
|
||||
return $this->repository->retrieveColumn($this->target, $column, $result, $this);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
@ -425,7 +425,7 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
$alias = $column;
|
||||
}
|
||||
|
||||
$result->$alias = $this->repository->retrieveColumn($this->target, $alias, $result->$alias);
|
||||
$result->$alias = $this->repository->retrieveColumn($this->target, $alias, $result->$alias, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,7 +450,7 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
$column = is_int($aliases[0]) ? $columns[0] : $aliases[0];
|
||||
if ($this->repository->providesValueConversion($this->target, $column)) {
|
||||
foreach ($results as & $value) {
|
||||
$value = $this->repository->retrieveColumn($this->target, $column, $value);
|
||||
$value = $this->repository->retrieveColumn($this->target, $column, $value, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -483,8 +483,13 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
) {
|
||||
$newResults = array();
|
||||
foreach ($results as $colOneValue => $colTwoValue) {
|
||||
$colOneValue = $this->repository->retrieveColumn($this->target, $colOne, $colOneValue);
|
||||
$newResults[$colOneValue] = $this->repository->retrieveColumn($this->target, $colTwo, $colTwoValue);
|
||||
$colOneValue = $this->repository->retrieveColumn($this->target, $colOne, $colOneValue, $this);
|
||||
$newResults[$colOneValue] = $this->repository->retrieveColumn(
|
||||
$this->target,
|
||||
$colTwo,
|
||||
$colTwoValue,
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
$results = $newResults;
|
||||
|
@ -516,7 +521,7 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
$alias = $column;
|
||||
}
|
||||
|
||||
$row->$alias = $this->repository->retrieveColumn($this->target, $alias, $row->$alias);
|
||||
$row->$alias = $this->repository->retrieveColumn($this->target, $alias, $row->$alias, $this);
|
||||
}
|
||||
|
||||
foreach (($this->getOrder() ?: array()) as $rule) {
|
||||
|
@ -591,7 +596,7 @@ class RepositoryQuery implements QueryInterface, Iterator
|
|||
$alias = $column;
|
||||
}
|
||||
|
||||
$row->$alias = $this->repository->retrieveColumn($this->target, $alias, $row->$alias);
|
||||
$row->$alias = $this->repository->retrieveColumn($this->target, $alias, $row->$alias, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ class JavaScript
|
|||
'js/icinga/behavior/sparkline.js',
|
||||
'js/icinga/behavior/tristate.js',
|
||||
'js/icinga/behavior/navigation.js',
|
||||
'js/icinga/behavior/form.js'
|
||||
'js/icinga/behavior/form.js',
|
||||
'js/icinga/behavior/actiontable.js'
|
||||
);
|
||||
|
||||
protected static $vendorFiles = array(
|
||||
|
|
|
@ -8,7 +8,7 @@ use Icinga\Module\Monitoring\Forms\Command\CommandForm;
|
|||
use Icinga\Web\Notification;
|
||||
|
||||
/**
|
||||
* Form for enabling or disabling features of Icinga objects, i.e. hosts or services
|
||||
* Form for enabling or disabling features of Icinga instances
|
||||
*/
|
||||
class ToggleInstanceFeaturesCommandForm extends CommandForm
|
||||
{
|
||||
|
@ -79,65 +79,68 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
} else {
|
||||
$notificationDescription = null;
|
||||
}
|
||||
|
||||
$toggleDisabled = $this->hasPermission('monitoring/command/feature/instance') ? null : '';
|
||||
$this->addElements(array(
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS,
|
||||
array(
|
||||
'label' => $this->translate('Active Host Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
'label' => $this->translate('Active Host Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS,
|
||||
array(
|
||||
'label' => $this->translate('Active Service Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
'label' => $this->translate('Active Service Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS,
|
||||
array(
|
||||
'label' => $this->translate('Event Handlers'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
'label' => $this->translate('Event Handlers'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION,
|
||||
array(
|
||||
'label' => $this->translate('Flap Detection'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
'label' => $this->translate('Flap Detection'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS,
|
||||
array(
|
||||
'label' => $this->translate('Notifications'),
|
||||
'autosubmit' => true,
|
||||
'description' => $notificationDescription,
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
'Errors',
|
||||
array(
|
||||
'Description',
|
||||
array('tag' => 'span', 'class' => 'description', 'escape' => false)
|
||||
),
|
||||
'Label',
|
||||
array('HtmlTag', array('tag' => 'div'))
|
||||
'label' => $this->translate('Notifications'),
|
||||
'autosubmit' => true,
|
||||
'description' => $notificationDescription,
|
||||
'decorators' => array(
|
||||
'ViewHelper',
|
||||
'Errors',
|
||||
array(
|
||||
'Description',
|
||||
array('tag' => 'span', 'class' => 'description', 'escape' => false)
|
||||
),
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
'Label',
|
||||
array('HtmlTag', array('tag' => 'div'))
|
||||
),
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
|
||||
if (! preg_match('~^v2\.\d+\.\d+.*$~', $this->status->program_version)) {
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING,
|
||||
array(
|
||||
|
@ -145,8 +148,8 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING,
|
||||
array(
|
||||
|
@ -154,8 +157,8 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS,
|
||||
array(
|
||||
|
@ -163,8 +166,8 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS,
|
||||
array(
|
||||
|
@ -172,18 +175,18 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA,
|
||||
array(
|
||||
'label' => $this->translate('Performance Data'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
'label' => $this->translate('Performance Data'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
));
|
||||
return $this;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,6 +202,7 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
foreach ($this->getValues() as $feature => $enabled) {
|
||||
$this->getElement($feature)->setChecked($instanceStatus->{$feature});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -254,18 +258,19 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
|
|||
);
|
||||
|
||||
foreach ($this->getValues() as $feature => $enabled) {
|
||||
$toggleFeature = new ToggleInstanceFeatureCommand();
|
||||
$toggleFeature
|
||||
->setFeature($feature)
|
||||
->setEnabled($enabled);
|
||||
$this->getTransport($this->request)->send($toggleFeature);
|
||||
|
||||
if ((bool) $this->status->{$feature} !== (bool) $enabled) {
|
||||
$toggleFeature = new ToggleInstanceFeatureCommand();
|
||||
$toggleFeature
|
||||
->setFeature($feature)
|
||||
->setEnabled($enabled);
|
||||
$this->getTransport($this->request)->send($toggleFeature);
|
||||
|
||||
Notification::success(
|
||||
$notifications[$feature][$enabled ? 0 : 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ class DeleteCommentCommandForm extends CommandForm
|
|||
'escape' => false,
|
||||
'type' => 'submit',
|
||||
'class' => 'link-like',
|
||||
'label' => $this->getView()->icon('cancel'),
|
||||
'label' => $this->getView()->icon('trash'),
|
||||
'title' => $this->translate('Delete this comment'),
|
||||
'decorators' => array('ViewHelper')
|
||||
)
|
||||
|
|
|
@ -31,26 +31,28 @@ class ToggleObjectFeaturesCommandForm extends ObjectsCommandForm
|
|||
public function createElements(array $formData = array())
|
||||
{
|
||||
$toggleDisabled = $this->hasPermission('monitoring/command/feature/object') ? null : '';
|
||||
$this->addElements(array(
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS,
|
||||
array(
|
||||
'label' => $this->translate('Active Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS,
|
||||
array(
|
||||
'label' => $this->translate('Passive Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
'label' => $this->translate('Active Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS,
|
||||
array(
|
||||
'label' => $this->translate('Passive Checks'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
|
||||
if (! preg_match('~^v2\.\d+\.\d+.*$~', $this->getIcingaVersion())) {
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_OBSESSING,
|
||||
array(
|
||||
|
@ -58,36 +60,36 @@ class ToggleObjectFeaturesCommandForm extends ObjectsCommandForm
|
|||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS,
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS,
|
||||
array(
|
||||
'label' => $this->translate('Notifications'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER,
|
||||
array(
|
||||
'label' => $this->translate('Event Handler'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
),
|
||||
array(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION,
|
||||
array(
|
||||
'label' => $this->translate('Flap Detection'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
'label' => $this->translate('Notifications'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
));
|
||||
return $this;
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER,
|
||||
array(
|
||||
'label' => $this->translate('Event Handler'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION,
|
||||
array(
|
||||
'label' => $this->translate('Flap Detection'),
|
||||
'autosubmit' => true,
|
||||
'disabled' => $toggleDisabled
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,6 +109,7 @@ class ToggleObjectFeaturesCommandForm extends ObjectsCommandForm
|
|||
$element->setDescription($this->translate('changed'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -162,6 +165,17 @@ class ToggleObjectFeaturesCommandForm extends ObjectsCommandForm
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and return the program version of the current instance
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getIcingaVersion()
|
||||
{
|
||||
return $this->getBackend()->select()->from('programstatus', array('program_version'))->fetchOne();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -377,7 +377,7 @@ class BackendConfigForm extends ConfigForm
|
|||
$rowCount = $db->select()->from('icinga_instances')->count();
|
||||
|
||||
if ($rowCount === 0) {
|
||||
$form->error($form->translate(
|
||||
$form->warning($form->translate(
|
||||
'There is currently no icinga instance writing to the IDO. Make sure '
|
||||
. 'that a icinga instance is configured and able to write to the IDO.'
|
||||
));
|
||||
|
|
|
@ -20,8 +20,13 @@ class InstancePage extends Form
|
|||
public function createElements(array $formData)
|
||||
{
|
||||
$instanceConfigForm = new InstanceConfigForm();
|
||||
$instanceConfigForm->createElements($formData);
|
||||
$this->addElements($instanceConfigForm->getElements());
|
||||
$this->getElement('name')->setValue('icinga');
|
||||
$this->addSubForm($instanceConfigForm, 'instance_form');
|
||||
$instanceConfigForm->create($formData);
|
||||
$instanceConfigForm->getElement('name')->setValue('icinga');
|
||||
}
|
||||
|
||||
public function getValues($suppressArrayNotation = false)
|
||||
{
|
||||
return $this->getSubForm('instance_form')->getValues($suppressArrayNotation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class Zend_View_Helper_Link extends Zend_View_Helper_Abstract
|
|||
$linkText,
|
||||
'monitoring/host/show',
|
||||
array('host' => $host),
|
||||
array('title' => sprintf($this->view->translate('Show detailed information for host %s'), $host))
|
||||
array('title' => sprintf($this->view->translate('Show detailed information for host %s'), $linkText))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -45,10 +45,11 @@ class Zend_View_Helper_Link extends Zend_View_Helper_Abstract
|
|||
* @param string $serviceLinkText Text for the service link, e.g. the service's display name
|
||||
* @param string $host Hostname
|
||||
* @param string $hostLinkText Text for the host link, e.g. the host's display name
|
||||
* @param string $class An optional class to use for this link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function service($service, $serviceLinkText, $host, $hostLinkText)
|
||||
public function service($service, $serviceLinkText, $host, $hostLinkText, $class = null)
|
||||
{
|
||||
return sprintf(
|
||||
'%s: %s',
|
||||
|
@ -57,11 +58,14 @@ class Zend_View_Helper_Link extends Zend_View_Helper_Abstract
|
|||
$serviceLinkText,
|
||||
'monitoring/service/show',
|
||||
array('host' => $host, 'service' => $service),
|
||||
array('title' => sprintf(
|
||||
$this->view->translate('Show detailed information for service %s on host %s'),
|
||||
$service,
|
||||
$host
|
||||
))
|
||||
array(
|
||||
'title' => sprintf(
|
||||
$this->view->translate('Show detailed information for service %s on host %s'),
|
||||
$serviceLinkText,
|
||||
$hostLinkText
|
||||
),
|
||||
'class' => $class
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract
|
|||
$pieChartData = PerfdataSet::fromString($perfdataStr)->asArray();
|
||||
uasort(
|
||||
$pieChartData,
|
||||
function($a, $b) {
|
||||
function ($a, $b) {
|
||||
return $a->worseThan($b) ? -1 : ($b->worseThan($a) ? 1 : 0);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -27,24 +27,29 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract
|
|||
'@@@@@@',
|
||||
);
|
||||
|
||||
public function pluginOutput($output)
|
||||
public function pluginOutput($output, $raw = false)
|
||||
{
|
||||
if (empty($output)) {
|
||||
return '';
|
||||
}
|
||||
$output = preg_replace('~<br[^>]+>~', "\n", $output);
|
||||
if (preg_match('~<\w+[^>^\\\]{,60}>~', $output)) {
|
||||
$output = preg_replace('~<br[^>]*>~', "\n", $output);
|
||||
if (strlen($output) > strlen(strip_tags($output))) {
|
||||
// HTML
|
||||
$output = preg_replace('~<table~', '<table style="font-size: 0.75em"',
|
||||
$output = preg_replace(
|
||||
'~<table~',
|
||||
'<table style="font-size: 0.75em"',
|
||||
$this->getPurifier()->purify($output)
|
||||
);
|
||||
} else {
|
||||
// Plaintext
|
||||
$output = '<pre class="pluginoutput">' . preg_replace(
|
||||
$output = preg_replace(
|
||||
self::$txtPatterns,
|
||||
self::$txtReplacements,
|
||||
$this->view->escape($output)
|
||||
) . '</pre>';
|
||||
);
|
||||
}
|
||||
if (! $raw) {
|
||||
$output = '<pre class="pluginoutput">' . $output . '</pre>';
|
||||
}
|
||||
$output = $this->fixLinks($output);
|
||||
return $output;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="content multi-commands">
|
||||
<h3><?= $this->icon('reschedule') ?> <?= $this->translate('Commands') ?> </h3>
|
||||
<h3><?= $this->translate('Commands') ?> </h3>
|
||||
<?= $this->qlink(
|
||||
sprintf(
|
||||
$this->translate('Remove all %d scheduled downtimes'),
|
||||
|
@ -17,8 +17,7 @@
|
|||
$removeAllLink,
|
||||
null,
|
||||
array(
|
||||
'icon' => 'trash',
|
||||
'title' => $this->translate('Remove all selected downtimes.')
|
||||
'icon' => 'trash'
|
||||
)
|
||||
) ?>
|
||||
</div>
|
||||
|
|
|
@ -24,41 +24,38 @@ if (count($comments) === 0) {
|
|||
data-icinga-multiselect-url="/icingaweb2/monitoring/comments/show"
|
||||
data-icinga-multiselect-data="comment_id">
|
||||
<tbody>
|
||||
<?php foreach ($comments as $comment):
|
||||
$this->comment = $comment; ?>
|
||||
|
||||
<?php foreach ($comments as $comment): ?>
|
||||
<tr class="state invalid">
|
||||
<td class="state" style="width: 12em;">
|
||||
<?= $this->render('partials/comment/comment-description.phtml'); ?>
|
||||
<?= $this->partial('partials/comment/comment-description.phtml', array('comment' => $comment)); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($comment->objecttype === 'service'): ?>
|
||||
<?= $this->icon('service', $this->translate('Service')); ?>
|
||||
|
||||
<?= $this->qlink(
|
||||
sprintf(
|
||||
'%s: %s',
|
||||
$comment->host_display_name,
|
||||
$comment->service_display_name
|
||||
),
|
||||
<?= $this->icon('service', $this->translate('Service')); ?> <?= $this->qlink(
|
||||
$this->escape($comment->host_display_name) . ': ' . $this->escape($comment->service_display_name),
|
||||
'monitoring/comment/show',
|
||||
array('comment_id' => $comment->id),
|
||||
array('title' => sprintf(
|
||||
$this->translate('Show detailed information for comment on %s for %s'),
|
||||
array(
|
||||
'title' => sprintf(
|
||||
$this->translate('Show detailed information for this comment about service %s on host %s'),
|
||||
$comment->service_display_name,
|
||||
$comment->host_display_name
|
||||
))) ?>
|
||||
),
|
||||
'class' => 'rowaction'
|
||||
)
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->icon('host', $this->translate('Host')); ?>
|
||||
|
||||
<?= $this->qlink(
|
||||
$comment->host_display_name,
|
||||
<?= $this->icon('host', $this->translate('Host')); ?> <?= $this->qlink(
|
||||
$this->escape($comment->host_display_name),
|
||||
'monitoring/comment/show',
|
||||
array('comment_id' => $comment->id),
|
||||
array('title' => sprintf(
|
||||
$this->translate('Show detailed information for comment on %s'),
|
||||
array(
|
||||
'title' => sprintf(
|
||||
$this->translate('Show detailed information for this comment about host %s'),
|
||||
$comment->host_display_name
|
||||
))) ?>
|
||||
)
|
||||
)
|
||||
); ?>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<?= $this->icon('comment', $this->translate('Comment')); ?> <?= isset($comment->author)
|
||||
|
|
|
@ -46,22 +46,34 @@ if (count($downtimes) === 0) {
|
|||
<?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if ($isService) {
|
||||
echo $this->icon('service');
|
||||
} else {
|
||||
echo $this->icon('host');
|
||||
}
|
||||
?>
|
||||
<?= $this->qlink(
|
||||
sprintf('%s: %s', $downtime->host_display_name, $downtime->service_display_name),
|
||||
'monitoring/downtime/show',
|
||||
array('downtime_id' => $downtime->id),
|
||||
array('title' => sprintf(
|
||||
$this->translate('Show detailed information for downtime on %s for %s'),
|
||||
<?php if ($isService): ?>
|
||||
<?= $this->icon('service', $this->translate('Service')); ?> <?= $this->qlink(
|
||||
$this->escape($downtime->host_display_name) . ': ' . $this->escape($downtime->service_display_name),
|
||||
'monitoring/downtime/show',
|
||||
array('downtime_id' => $downtime->id),
|
||||
array(
|
||||
'title' => sprintf(
|
||||
$this->translate('Show detailed information for this downtime scheduled for service %s on host %s'),
|
||||
$downtime->service_display_name,
|
||||
$downtime->host_display_name
|
||||
))) ?>
|
||||
),
|
||||
'class' => 'rowaction'
|
||||
)
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->icon('host', $this->translate('host')); ?> <?= $this->qlink(
|
||||
$this->escape($downtime->host_display_name),
|
||||
'monitoring/downtime/show',
|
||||
array('downtime_id' => $downtime->id),
|
||||
array(
|
||||
'title' => sprintf(
|
||||
$this->translate('Show detailed information for this downtime scheduled for host %s'),
|
||||
$downtime->host_display_name
|
||||
),
|
||||
'class' => 'rowaction'
|
||||
)
|
||||
); ?>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<?= $this->icon('comment', $this->translate('Comment')); ?> [<?= $this->escape($downtime->author_name) ?>] <?= $this->escape($downtime->comment) ?>
|
||||
<br>
|
||||
|
|
|
@ -82,7 +82,7 @@ if (count($history) === 0) {
|
|||
<td>
|
||||
<?php if ($isService): ?>
|
||||
<?= $this->link()->service(
|
||||
$event->service_description, $event->service_display_name, $event->host_name, $event->host_display_name
|
||||
$event->service_description, $event->service_display_name, $event->host_name, $event->host_display_name, 'rowaction'
|
||||
) ?>
|
||||
<?php else: ?>
|
||||
<?= $this->link()->host($event->host_name, $event->host_display_name) ?>
|
||||
|
|
|
@ -60,7 +60,8 @@ if (count($hosts) === 0) {
|
|||
$hostLink,
|
||||
null,
|
||||
array(
|
||||
'title' => sprintf($this->translate('Show detailed information for host %s'), $host->host_display_name)
|
||||
'title' => sprintf($this->translate('Show detailed information for host %s'), $host->host_display_name),
|
||||
'class' => 'rowaction'
|
||||
)
|
||||
); ?>
|
||||
<?php if (isset($host->host_unhandled_services) && $host->host_unhandled_services > 0): ?>
|
||||
|
@ -89,7 +90,7 @@ if (count($hosts) === 0) {
|
|||
)
|
||||
) ?>)</span>
|
||||
<?php endif ?>
|
||||
<p class="pluginoutput"><?= $this->escape($this->ellipsis($host->host_output, 10000)) ?></p>
|
||||
<p class="pluginoutput"><?= $this->pluginOutput($this->ellipsis($host->host_output, 10000), true) ?></p>
|
||||
</td>
|
||||
<?php foreach($this->addColumns as $col): ?>
|
||||
<td><?= $this->escape($host->$col) ?></td>
|
||||
|
|
|
@ -48,7 +48,7 @@ if (count($notifications) === 0) {
|
|||
<?= $this->link()->host($notification->host_name, $notification->host_display_name) ?>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<?= $this->escape($this->ellipsis($notification->notification_output, 10000)) ?>
|
||||
<?= $this->pluginOutput($this->ellipsis($notification->notification_output, 10000), true) ?>
|
||||
<br>
|
||||
<?php if (! $this->contact): ?>
|
||||
<small>
|
||||
|
|
|
@ -82,7 +82,7 @@ if (count($services) === 0) {
|
|||
)
|
||||
) ?><br />
|
||||
<div class="sparkline-box"><?= $this->perfdata($service->service_perfdata, true, 5) ?> </div>
|
||||
<p class="pluginoutput"><?= $this->escape($this->ellipsis($service->service_output, 10000)) ?></p>
|
||||
<p class="pluginoutput"><?= $this->pluginOutput($this->ellipsis($service->service_output, 10000), true) ?></p>
|
||||
</td>
|
||||
<?php foreach($this->addColumns as $col): ?>
|
||||
<td><?= $this->escape($service->$col) ?></td>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<table class="action">
|
||||
<table class="action" data-base-target="_next">
|
||||
<tbody>
|
||||
<?php $i = 0; foreach ($downtimes as $downtime):
|
||||
if (++ $i > 5) {
|
||||
continue;
|
||||
break;
|
||||
} ?>
|
||||
<tr class="state <?= $downtime->stateText ?>">
|
||||
<td class="state">
|
||||
|
@ -13,16 +13,21 @@
|
|||
<td class="name oneline">
|
||||
<?php if ($downtime->isService): ?>
|
||||
<?= $this->icon('service', $this->translate('Service')) ?>
|
||||
<b><?= $downtime->service ?> on <?= $downtime->host_name ?>.</b>
|
||||
<?= $this->link()->service(
|
||||
$downtime->service_description,
|
||||
$downtime->service_display_name,
|
||||
$downtime->host_name,
|
||||
$downtime->host_display_name
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->icon('host', $this->translate('Host')) ?>
|
||||
<b><?= $downtime->host_name ?>.</b>
|
||||
<?= $this->link()->host($downtime->host_name, $downtime->host_display_name); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<br>
|
||||
<?php if ($downtime->is_flexible): ?>
|
||||
<?php if ($downtime->is_in_effect): ?>
|
||||
<?= sprintf(
|
||||
$this->isService
|
||||
$downtime->isService
|
||||
? $this->translate('This flexible service downtime was started on %s at %s and lasts for %s until %s at %s.')
|
||||
: $this->translate('This flexible host downtime was started on %s at %s and lasts for %s until %s at %s.'),
|
||||
$this->formatDate($downtime->start),
|
||||
|
@ -33,7 +38,7 @@
|
|||
); ?>
|
||||
<?php else: ?>
|
||||
<?= sprintf(
|
||||
$this->isService
|
||||
$downtime->isService
|
||||
? $this->translate('This flexible service downtime has been scheduled to start between %s - %s and to last for %s.')
|
||||
: $this->translate('This flexible host downtime has been scheduled to start between %s - %s and to last for %s.'),
|
||||
$this->formatDateTime($downtime->scheduled_start),
|
||||
|
@ -44,7 +49,7 @@
|
|||
<?php else: ?>
|
||||
<?php if ($downtime->is_in_effect): ?>
|
||||
<?= sprintf(
|
||||
$this->isService
|
||||
$downtime->isService
|
||||
? $this->translate('This fixed service downtime was started on %s at %s and expires on %s at %s.')
|
||||
: $this->translate('This fixed host downtime was started on %s at %s and expires on %s at %s.'),
|
||||
$this->formatDate($downtime->start),
|
||||
|
@ -54,13 +59,13 @@
|
|||
); ?>
|
||||
<?php else: ?>
|
||||
<?= sprintf(
|
||||
$this->isService
|
||||
$downtime->isService
|
||||
? $this->translate('This fixed service downtime has been scheduled to start on %s at %s and to end on %s at %s.')
|
||||
: $this->translate('This fixed host downtime has been scheduled to start on %s at %s and to end on %s at %s.'),
|
||||
$this->formatDate($downtime->start),
|
||||
$this->formatTime($downtime->start),
|
||||
$this->formatDate($downtime->end),
|
||||
$this->formatTime($downtime->end)
|
||||
$this->formatDate($downtime->scheduled_start),
|
||||
$this->formatTime($downtime->scheduled_start),
|
||||
$this->formatDate($downtime->scheduled_end),
|
||||
$this->formatTime($downtime->scheduled_end)
|
||||
); ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
@ -68,18 +73,18 @@
|
|||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<?php if ($i > 5): ?>
|
||||
<?php if (count($downtimes) > 5): ?>
|
||||
<p>
|
||||
<?= $this->qlink(
|
||||
sprintf($this->translate('show all %d downtimes'), $i),
|
||||
sprintf($this->translate('List all %d downtimes'), $i),
|
||||
$listAllLink,
|
||||
null,
|
||||
array(
|
||||
'icon' => $i > 5 ? 'down-open' : '',
|
||||
'data-base-target' => "_next"
|
||||
'icon' => 'down-open',
|
||||
'data-base-target' => "_next"
|
||||
)
|
||||
) ?>
|
||||
<?php endif ?>
|
||||
</p>
|
||||
</p>
|
||||
<?php endif ?>
|
|
@ -136,28 +136,35 @@ class BackendStep extends Step
|
|||
|
||||
public function getReport()
|
||||
{
|
||||
$report = '';
|
||||
$report = array();
|
||||
|
||||
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,
|
||||
$report[] = sprintf(
|
||||
mt('monitoring', 'Monitoring backend configuration has been successfully written to: %s'),
|
||||
Config::resolvePath('modules/monitoring/backends.ini')
|
||||
) . '</p><p>' . $this->backendIniError->getMessage() . '</p>';
|
||||
);
|
||||
} elseif ($this->backendIniError !== null) {
|
||||
$report[] = sprintf(
|
||||
mt(
|
||||
'monitoring',
|
||||
'Monitoring backend configuration could not be written to: %s. An error occured:'
|
||||
),
|
||||
Config::resolvePath('modules/monitoring/backends.ini')
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->backendIniError->getMessage());
|
||||
}
|
||||
|
||||
if ($this->resourcesIniError === false) {
|
||||
$message = mt('monitoring', 'Resource configuration has been successfully updated: %s');
|
||||
$report .= '<p>' . sprintf($message, Config::resolvePath('resources.ini')) . '</p>';
|
||||
$report[] = sprintf(
|
||||
mt('monitoring', 'Resource configuration has been successfully updated: %s'),
|
||||
Config::resolvePath('resources.ini')
|
||||
);
|
||||
} 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>';
|
||||
$report[] = sprintf(
|
||||
mt('monitoring', 'Resource configuration could not be udpated: %s. An error occured:'),
|
||||
Config::resolvePath('resources.ini')
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->resourcesIniError->getMessage());
|
||||
}
|
||||
|
||||
return $report;
|
||||
|
|
|
@ -85,15 +85,21 @@ class InstanceStep extends Step
|
|||
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>';
|
||||
return array(sprintf(
|
||||
mt('monitoring', 'Monitoring instance configuration has been successfully created: %s'),
|
||||
Config::resolvePath('modules/monitoring/instances.ini')
|
||||
));
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt(
|
||||
'monitoring',
|
||||
'Monitoring instance configuration could not be written to: %s; An error occured:'
|
||||
return array(
|
||||
sprintf(
|
||||
mt(
|
||||
'monitoring',
|
||||
'Monitoring instance configuration could not be written to: %s. An error occured:'
|
||||
),
|
||||
Config::resolvePath('modules/monitoring/instances.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
);
|
||||
return '<p class="error">' . sprintf($message, Config::resolvePath('modules/monitoring/instances.ini'))
|
||||
. '</p><p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use Icinga\Module\Setup\Requirement\PhpModuleRequirement;
|
|||
class MonitoringWizard extends Wizard implements SetupWizard
|
||||
{
|
||||
/**
|
||||
* @see Wizard::init()
|
||||
* Register all pages for this wizard
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
|
@ -39,7 +39,10 @@ class MonitoringWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::setupPage()
|
||||
* Setup the given page that is either going to be displayed or validated
|
||||
*
|
||||
* @param Form $page The page to setup
|
||||
* @param Request $request The current request
|
||||
*/
|
||||
public function setupPage(Form $page, Request $request)
|
||||
{
|
||||
|
@ -52,18 +55,30 @@ class MonitoringWizard extends Wizard implements SetupWizard
|
|||
$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'))
|
||||
if (
|
||||
(($authDbResourceData = $this->getPageData('setup_auth_db_resource')) !== null
|
||||
&& $authDbResourceData['name'] === $request->getPost('name'))
|
||||
|| (($configDbResourceData = $this->getPageData('setup_config_db_resource')) !== null
|
||||
&& $configDbResourceData['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.'));
|
||||
$page->error(mt('monitoring', 'The given resource name is already in use.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::getNewPage()
|
||||
* Return the new page to set as current page
|
||||
*
|
||||
* {@inheritdoc} Runs additional checks related to some registered pages.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
|
@ -81,7 +96,9 @@ class MonitoringWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::addButtons()
|
||||
* 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)
|
||||
{
|
||||
|
@ -100,7 +117,9 @@ class MonitoringWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getSetup()
|
||||
* Return the setup for this wizard
|
||||
*
|
||||
* @return Setup
|
||||
*/
|
||||
public function getSetup()
|
||||
{
|
||||
|
@ -132,7 +151,9 @@ class MonitoringWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getRequirements()
|
||||
* Return the requirements of this wizard
|
||||
*
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function getRequirements()
|
||||
{
|
||||
|
|
|
@ -124,9 +124,14 @@ abstract class ObjectList implements Countable, IteratorAggregate, Filterable
|
|||
public function count()
|
||||
{
|
||||
if ($this->count === null) {
|
||||
$this->count = (int) $this->backend->select()->from($this->dataViewName)->applyFilter($this->filter)
|
||||
->getQuery()->count();
|
||||
$this->count = (int) $this->backend
|
||||
->select()
|
||||
->from($this->dataViewName, $this->columns)
|
||||
->applyFilter($this->filter)
|
||||
->getQuery()
|
||||
->count();
|
||||
}
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,15 +63,21 @@ class SecurityStep extends Step
|
|||
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>';
|
||||
return array(sprintf(
|
||||
mt('monitoring', 'Monitoring security configuration has been successfully created: %s'),
|
||||
Config::resolvePath('modules/monitoring/config.ini')
|
||||
));
|
||||
} elseif ($this->error !== null) {
|
||||
$message = mt(
|
||||
'monitoring',
|
||||
'Monitoring security configuration could not be written to: %s; An error occured:'
|
||||
return array(
|
||||
sprintf(
|
||||
mt(
|
||||
'monitoring',
|
||||
'Monitoring security configuration could not be written to: %s. An error occured:'
|
||||
),
|
||||
Config::resolvePath('modules/monitoring/config.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
);
|
||||
return '<p class="error">' . sprintf($message, Config::resolvePath('modules/monitoring/config.ini'))
|
||||
. '</p><p>' . $this->error->getMessage() . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ abstract class MonitoredObjectController extends Controller
|
|||
$this->object->populate();
|
||||
$toggleFeaturesForm = new ToggleObjectFeaturesCommandForm();
|
||||
$toggleFeaturesForm
|
||||
->setBackend($this->backend)
|
||||
->load($this->object)
|
||||
->setObjects($this->object)
|
||||
->handleRequest();
|
||||
|
|
|
@ -165,21 +165,25 @@ class AdminAccountPage extends Form
|
|||
'password',
|
||||
'new_user_password',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Password'),
|
||||
'description' => $this->translate('Enter the password to assign to the newly created account.')
|
||||
'required' => true,
|
||||
'renderPassword' => true,
|
||||
'label' => $this->translate('Password'),
|
||||
'description' => $this->translate(
|
||||
'Enter the password to assign to the newly created account.'
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'password',
|
||||
'new_user_2ndpass',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Repeat password'),
|
||||
'description' => $this->translate(
|
||||
'required' => true,
|
||||
'renderPassword' => true,
|
||||
'label' => $this->translate('Repeat password'),
|
||||
'description' => $this->translate(
|
||||
'Please repeat the password given above to avoid typing errors.'
|
||||
),
|
||||
'validators' => array(
|
||||
'validators' => array(
|
||||
array('identical', false, array('new_user_password'))
|
||||
)
|
||||
)
|
||||
|
@ -200,7 +204,7 @@ class AdminAccountPage extends Form
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($data['user_type'] === 'new_user' && !$this->hasUser($data['new_user'])) {
|
||||
if ($data['user_type'] === 'new_user' && $this->hasUser($data['new_user'])) {
|
||||
$this->getElement('new_user')->addError($this->translate('Username already exists.'));
|
||||
return false;
|
||||
}
|
||||
|
@ -253,7 +257,11 @@ class AdminAccountPage extends Form
|
|||
*/
|
||||
protected function hasUser($username)
|
||||
{
|
||||
return $this->createBackend()->select()->where('user_name', $username)->count() > 1;
|
||||
try {
|
||||
return $this->createBackend()->select()->where('user_name', $username)->count() > 1;
|
||||
} catch (Exception $_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -121,7 +121,7 @@ class AuthBackendPage extends Form
|
|||
return false;
|
||||
}
|
||||
|
||||
if (false === isset($data['skip_validation']) || $data['skip_validation'] == 0) {
|
||||
if ($this->config['type'] === 'ldap' && ( !isset($data['skip_validation']) || $data['skip_validation'] == 0)) {
|
||||
$self = clone $this;
|
||||
$self->addElement(
|
||||
'text',
|
||||
|
@ -130,7 +130,7 @@ class AuthBackendPage extends Form
|
|||
'value' => $this->getResourceConfig()
|
||||
)
|
||||
);
|
||||
if ($this->config['type'] === 'ldap' && false === LdapBackendForm::isValidUserBackend($self)) {
|
||||
if (! LdapBackendForm::isValidUserBackend($self)) {
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ class DatabaseCreationPage extends Form
|
|||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_database_creation');
|
||||
$this->setTitle($this->translate('Database Setup', 'setup.page.title'));
|
||||
$this->addDescription($this->translate(
|
||||
'It seems that either the database you defined earlier does not yet exist and cannot be created'
|
||||
|
@ -108,8 +107,9 @@ class DatabaseCreationPage extends Form
|
|||
'password',
|
||||
'password',
|
||||
array(
|
||||
'label' => $this->translate('Password'),
|
||||
'description' => $this->translate('The password for the database user defined above')
|
||||
'renderPassword' => true,
|
||||
'label' => $this->translate('Password'),
|
||||
'description' => $this->translate('The password for the database user defined above')
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -156,7 +156,7 @@ class DatabaseCreationPage extends Form
|
|||
$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->error($e->getMessage());
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ class DatabaseCreationPage extends Form
|
|||
// 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(
|
||||
$this->error(
|
||||
$this->translate('The provided credentials cannot be used to create the database and/or the user.')
|
||||
);
|
||||
$this->addSkipValidationCheckbox();
|
||||
|
@ -174,7 +174,7 @@ class DatabaseCreationPage extends Form
|
|||
|
||||
// ...and to grant all required usage privileges to others
|
||||
if (false === $db->isGrantable($this->databaseUsagePrivileges)) {
|
||||
$this->addError(sprintf(
|
||||
$this->error(sprintf(
|
||||
$this->translate(
|
||||
'The provided credentials cannot be used to grant all required privileges to the login "%s".'
|
||||
),
|
||||
|
|
|
@ -18,7 +18,6 @@ class DbResourcePage extends Form
|
|||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('setup_db_resource');
|
||||
$this->setTitle($this->translate('Database Resource', 'setup.page.title'));
|
||||
$this->addDescription($this->translate(
|
||||
'Now please configure your database resource. Note that the database itself does not need to'
|
||||
|
@ -56,14 +55,6 @@ class DbResourcePage extends Form
|
|||
$resourceForm = new DbResourceForm();
|
||||
$this->addElements($resourceForm->createElements($formData)->getElements());
|
||||
$this->getElement('name')->setValue('icingaweb_db');
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'prefix',
|
||||
array(
|
||||
'required' => true,
|
||||
'value' => 'icingaweb_'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,7 +75,7 @@ class DbResourcePage extends Form
|
|||
$db = new DbTool($this->getValues());
|
||||
$db->checkConnectivity();
|
||||
} catch (PDOException $e) {
|
||||
$this->addError($e->getMessage());
|
||||
$this->error($e->getMessage());
|
||||
$this->addSkipValidationCheckbox();
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
namespace Icinga\Module\Setup\Forms;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Forms\Config\General\ApplicationConfigForm;
|
||||
use Icinga\Forms\Config\General\LoggingConfigForm;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Wizard page to define the application and logging configuration
|
||||
|
@ -28,7 +29,13 @@ class GeneralConfigPage extends Form
|
|||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$loggingForm = new LoggingConfigForm();
|
||||
$this->addElements($loggingForm->createElements($formData)->getElements());
|
||||
$appConfigForm = new ApplicationConfigForm();
|
||||
$appConfigForm->createElements($formData);
|
||||
$appConfigForm->removeElement('global_module_path');
|
||||
$appConfigForm->removeElement('global_config_resource');
|
||||
$this->addElements($appConfigForm->getElements());
|
||||
|
||||
$loggingConfigForm = new LoggingConfigForm();
|
||||
$this->addElements($loggingConfigForm->createElements($formData)->getElements());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class LdapDiscoveryPage extends Form
|
|||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
$this->addError(
|
||||
$this->error(
|
||||
sprintf($this->translate('Could not find any LDAP servers on the domain "%s".'), $data['domain'])
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Forms;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Application\Platform;
|
||||
|
||||
/**
|
||||
* Wizard page to choose a preference backend
|
||||
*/
|
||||
class PreferencesPage extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this page
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setRequiredCue(null);
|
||||
$this->setName('setup_preferences_type');
|
||||
$this->setTitle($this->translate('Preferences', 'setup.page.title'));
|
||||
$this->addDescription($this->translate('Please choose how Icinga Web 2 should store user preferences.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$storageTypes = array();
|
||||
$storageTypes['ini'] = $this->translate('File System (INI Files)');
|
||||
if (Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
|
||||
$storageTypes['db'] = $this->translate('Database');
|
||||
}
|
||||
$storageTypes['none'] = $this->translate('Don\'t Store Preferences');
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'store',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('User Preference Storage Type'),
|
||||
'multiOptions' => $storageTypes
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,22 +1,10 @@
|
|||
<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"><?= $this->translate('Congratulations! Icinga Web 2 has been successfully set up.'); ?></p>
|
||||
<?php else: ?>
|
||||
<p class="failure"><?= $this->translate('Sorry! Failed to set up Icinga Web 2 successfully.'); ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<?php if ($success): ?>
|
||||
<h2 class="success"><?= $this->translate('Congratulations! Icinga Web 2 has been successfully set up.'); ?></h2>
|
||||
<?php else: ?>
|
||||
<h2 class="failure"><?= $this->translate('Sorry! Failed to set up Icinga Web 2 successfully.'); ?></h2>
|
||||
<?php endif ?>
|
||||
<div class="buttons pull-right">
|
||||
<?php if ($success): ?>
|
||||
<?= $this->qlink(
|
||||
$this->translate('Login to Icinga Web 2'),
|
||||
|
@ -39,4 +27,7 @@
|
|||
); ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<textarea class="report" readonly><?= join("\n\n", array_map(function($a) {
|
||||
return join("\n", $a);
|
||||
}, $report)); ?></textarea>
|
||||
</div>
|
|
@ -9,53 +9,34 @@ class RequirementsRenderer extends RecursiveIteratorIterator
|
|||
{
|
||||
public function beginIteration()
|
||||
{
|
||||
$this->tags[] = '<table class="requirements">';
|
||||
$this->tags[] = '<tbody>';
|
||||
$this->tags[] = '<ul class="requirements">';
|
||||
}
|
||||
|
||||
public function endIteration()
|
||||
{
|
||||
$this->tags[] = '</tbody>';
|
||||
$this->tags[] = '</table>';
|
||||
$this->tags[] = '</ul>';
|
||||
}
|
||||
|
||||
public function beginChildren()
|
||||
{
|
||||
$this->tags[] = '<tr>';
|
||||
$this->tags[] = '<li>';
|
||||
$currentSet = $this->getSubIterator();
|
||||
$state = $currentSet->getState() ? 'fulfilled' : (
|
||||
$currentSet->isOptional() ? 'not-available' : 'missing'
|
||||
);
|
||||
$colSpanRequired = $this->hasSingleRequirements($this->getSubIterator($this->getDepth() - 1));
|
||||
$this->tags[] = '<td class="set-state ' . $state . '"' . ($colSpanRequired ? ' colspan=3' : '') . '>';
|
||||
$this->beginIteration();
|
||||
$state = $currentSet->getState() ? 'fulfilled' : ($currentSet->isOptional() ? 'not-available' : 'missing');
|
||||
$this->tags[] = '<ul class="set-state ' . $state . '">';
|
||||
}
|
||||
|
||||
public function endChildren()
|
||||
{
|
||||
$this->endIteration();
|
||||
$this->tags[] = '</td>';
|
||||
$this->tags[] = '</tr>';
|
||||
}
|
||||
|
||||
protected function hasSingleRequirements(RequirementSet $requirements)
|
||||
{
|
||||
$set = $requirements->getAll();
|
||||
foreach ($set as $entry) {
|
||||
if ($entry instanceof Requirement) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
$this->tags[] = '</ul>';
|
||||
$this->tags[] = '</li>';
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
foreach ($this as $requirement) {
|
||||
$this->tags[] = '<tr>';
|
||||
$this->tags[] = '<td class="title"><h2>' . $requirement->getTitle() . '</h2></td>';
|
||||
$this->tags[] = '<td class="desc">';
|
||||
$this->tags[] = '<li class="clearfix">';
|
||||
$this->tags[] = '<div class="title"><h3>' . $requirement->getTitle() . '</h3></div>';
|
||||
$this->tags[] = '<div class="description">';
|
||||
$descriptions = $requirement->getDescriptions();
|
||||
if (count($descriptions) > 1) {
|
||||
$this->tags[] = '<ul>';
|
||||
|
@ -66,11 +47,11 @@ class RequirementsRenderer extends RecursiveIteratorIterator
|
|||
} elseif (! empty($descriptions)) {
|
||||
$this->tags[] = $descriptions[0];
|
||||
}
|
||||
$this->tags[] = '</td>';
|
||||
$this->tags[] = '<td class="state ' . ($requirement->getState() ? 'fulfilled' : (
|
||||
$this->tags[] = '</div>';
|
||||
$this->tags[] = '<div class="state ' . ($requirement->getState() ? 'fulfilled' : (
|
||||
$requirement->isOptional() ? 'not-available' : 'missing'
|
||||
)) . '">' . $requirement->getStateText() . '</td>';
|
||||
$this->tags[] = '</tr>';
|
||||
)) . '">' . $requirement->getStateText() . '</div>';
|
||||
$this->tags[] = '</li>';
|
||||
}
|
||||
|
||||
return implode("\n", $this->tags);
|
||||
|
|
|
@ -81,13 +81,16 @@ class Setup implements IteratorAggregate
|
|||
/**
|
||||
* Return a report of all actions that were run
|
||||
*
|
||||
* @return array An array of HTML strings
|
||||
* @return array An array of arrays of strings
|
||||
*/
|
||||
public function getReport()
|
||||
{
|
||||
$reports = array();
|
||||
foreach ($this->steps as $step) {
|
||||
$reports[] = $step->getReport();
|
||||
$report = $step->getReport();
|
||||
if (! empty($report)) {
|
||||
$reports[] = $report;
|
||||
}
|
||||
}
|
||||
|
||||
return $reports;
|
||||
|
|
|
@ -23,9 +23,9 @@ abstract class Step
|
|||
abstract public function getSummary();
|
||||
|
||||
/**
|
||||
* Return a HTML representation of this step's configuration changes that were made
|
||||
* Return a textual summary of all configuration changes made
|
||||
*
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getReport();
|
||||
}
|
||||
|
|
|
@ -161,32 +161,45 @@ class AuthenticationStep extends Step
|
|||
|
||||
public function getReport()
|
||||
{
|
||||
$report = '';
|
||||
$report = array();
|
||||
|
||||
if ($this->authIniError === false) {
|
||||
$message = mt('setup', 'Authentication configuration has been successfully written to: %s');
|
||||
$report .= '<p>' . sprintf($message, Config::resolvePath('authentication.ini')) . '</p>';
|
||||
$report[] = sprintf(
|
||||
mt('setup', 'Authentication configuration has been successfully written to: %s'),
|
||||
Config::resolvePath('authentication.ini')
|
||||
);
|
||||
} 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>';
|
||||
$report[] = sprintf(
|
||||
mt('setup', 'Authentication configuration could not be written to: %s. An error occured:'),
|
||||
Config::resolvePath('authentication.ini')
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->authIniError->getMessage());
|
||||
}
|
||||
|
||||
if ($this->dbError === false) {
|
||||
$message = mt('setup', 'Account "%s" has been successfully created.');
|
||||
$report .= '<p>' . sprintf($message, $this->data['adminAccountData']['username']) . '</p>';
|
||||
$report[] = sprintf(
|
||||
mt('setup', 'Account "%s" has been successfully created.'),
|
||||
$this->data['adminAccountData']['username']
|
||||
);
|
||||
} 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>';
|
||||
$report[] = sprintf(
|
||||
mt('setup', 'Unable to create account "%s". An error occured:'),
|
||||
$this->data['adminAccountData']['username']
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->dbError->getMessage());
|
||||
}
|
||||
|
||||
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>';
|
||||
$report[] = sprintf(
|
||||
mt('setup', 'Account "%s" has been successfully defined as initial administrator.'),
|
||||
$this->data['adminAccountData']['username']
|
||||
);
|
||||
} 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>';
|
||||
$report[] = sprintf(
|
||||
mt('setup', 'Unable to define account "%s" as initial administrator. An error occured:'),
|
||||
$this->data['adminAccountData']['username']
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->permIniError->getMessage());
|
||||
}
|
||||
|
||||
return $report;
|
||||
|
|
|
@ -247,12 +247,14 @@ class DatabaseStep extends Step
|
|||
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>';
|
||||
$report = $this->messages;
|
||||
$report[] = mt('setup', 'The database has been fully set up!');
|
||||
return $report;
|
||||
} 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>';
|
||||
$report = $this->messages;
|
||||
$report[] = mt('setup', 'Failed to fully setup the database. An error occured:');
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage());
|
||||
return $report;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,13 +23,12 @@ class GeneralConfigStep extends Step
|
|||
{
|
||||
$config = array();
|
||||
foreach ($this->data['generalConfig'] as $sectionAndPropertyName => $value) {
|
||||
list($section, $property) = explode('_', $sectionAndPropertyName);
|
||||
list($section, $property) = explode('_', $sectionAndPropertyName, 2);
|
||||
$config[$section][$property] = $value;
|
||||
}
|
||||
|
||||
$config['preferences']['store'] = $this->data['preferencesStore'];
|
||||
if (isset($this->data['preferencesResource'])) {
|
||||
$config['preferences']['resource'] = $this->data['preferencesResource'];
|
||||
if ($config['global']['config_backend'] === 'db') {
|
||||
$config['global']['config_resource'] = $this->data['resourceName'];
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -54,14 +53,10 @@ class GeneralConfigStep extends Step
|
|||
$generalHtml = ''
|
||||
. '<ul>'
|
||||
. '<li>' . sprintf(
|
||||
$this->data['preferencesStore'] === 'ini' ? sprintf(
|
||||
$this->data['generalConfig']['global_config_backend'] === 'ini' ? sprintf(
|
||||
t('Preferences will be stored per user account in INI files at: %s'),
|
||||
Config::resolvePath('preferences')
|
||||
) : (
|
||||
$this->data['preferencesStore'] === 'db' ? t('Preferences will be stored using a database.') : (
|
||||
t('Preferences will not be persisted across browser sessions.')
|
||||
)
|
||||
)
|
||||
) : t('Preferences will be stored using a database.')
|
||||
) . '</li>'
|
||||
. '</ul>';
|
||||
|
||||
|
@ -107,12 +102,18 @@ class GeneralConfigStep extends Step
|
|||
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>';
|
||||
return array(sprintf(
|
||||
mt('setup', 'General configuration has been successfully written to: %s'),
|
||||
Config::resolvePath('config.ini')
|
||||
));
|
||||
} 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>';
|
||||
return array(
|
||||
sprintf(
|
||||
mt('setup', 'General configuration could not be written to: %s. An error occured:'),
|
||||
Config::resolvePath('config.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,12 +133,18 @@ class ResourceStep extends Step
|
|||
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>';
|
||||
return array(sprintf(
|
||||
mt('setup', 'Resource configuration has been successfully written to: %s'),
|
||||
Config::resolvePath('resources.ini')
|
||||
));
|
||||
} 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>';
|
||||
return array(
|
||||
sprintf(
|
||||
mt('setup', 'Resource configuration could not be written to: %s. An error occured:'),
|
||||
Config::resolvePath('resources.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,15 +167,15 @@ class DbTool
|
|||
*/
|
||||
protected function assertHostAccess()
|
||||
{
|
||||
if (false === isset($this->config['db'])) {
|
||||
if (! isset($this->config['db'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server of unknown type');
|
||||
} elseif (false === isset($this->config['host'])) {
|
||||
} elseif (! isset($this->config['host'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a hostname or address');
|
||||
} elseif (false === isset($this->config['port'])) {
|
||||
} elseif (! isset($this->config['port'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a port');
|
||||
} elseif (false === isset($this->config['username'])) {
|
||||
} elseif (! isset($this->config['username'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a username');
|
||||
} elseif (false === isset($this->config['password'])) {
|
||||
} elseif (! isset($this->config['password'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database server without a password');
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ class DbTool
|
|||
*/
|
||||
protected function assertDatabaseAccess()
|
||||
{
|
||||
if (false === isset($this->config['dbname'])) {
|
||||
if (! isset($this->config['dbname'])) {
|
||||
throw new ConfigurationError('Can\'t connect to database without a valid database name');
|
||||
}
|
||||
}
|
||||
|
@ -351,6 +351,24 @@ class DbTool
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given table name with all wildcards being escaped
|
||||
*
|
||||
* @param string $tableName
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws LogicException In case there is no behaviour implemented for the current PDO driver
|
||||
*/
|
||||
public function escapeTableWildcards($tableName)
|
||||
{
|
||||
if ($this->config['db'] === 'mysql') {
|
||||
return str_replace(array('_', '%'), array('\_', '\%'), $tableName);
|
||||
}
|
||||
|
||||
throw new LogicException('Unable to escape table wildcards.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given value escaped as string
|
||||
*
|
||||
|
@ -481,9 +499,12 @@ class DbTool
|
|||
{
|
||||
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']),
|
||||
$quotedDbName = $this->quoteIdentifier($this->config['dbname']);
|
||||
|
||||
$grant = 'GRANT %s';
|
||||
$on = ' ON %s.%s';
|
||||
$to = sprintf(
|
||||
' TO %s@%s',
|
||||
$this->quoteIdentifier($username),
|
||||
str_replace('%', '%%', $this->quoteIdentifier($host))
|
||||
);
|
||||
|
@ -491,36 +512,39 @@ class DbTool
|
|||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach (array_intersect($privileges, array_keys($this->mysqlGrantContexts)) as $privilege) {
|
||||
if (false === empty($context) && $this->mysqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
if (! empty($context) && $this->mysqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
} elseif ($this->mysqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
$dbPrivileges[] = $privilege;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($tablePrivileges)) {
|
||||
if (! empty($tablePrivileges)) {
|
||||
$tableGrant = sprintf($grant, join(',', $tablePrivileges));
|
||||
foreach ($context as $table) {
|
||||
$this->exec(
|
||||
sprintf($queryString, join(',', $tablePrivileges), $this->quoteIdentifier($table))
|
||||
);
|
||||
$this->exec($tableGrant . sprintf($on, $quotedDbName, $this->quoteIdentifier($table)) . $to);
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($dbPrivileges)) {
|
||||
$this->exec(sprintf($queryString, join(',', $dbPrivileges), '*'));
|
||||
if (! empty($dbPrivileges)) {
|
||||
$this->exec(
|
||||
sprintf($grant, join(',', $dbPrivileges))
|
||||
. sprintf($on, $this->escapeTableWildcards($quotedDbName), '*')
|
||||
. $to
|
||||
);
|
||||
}
|
||||
} 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) {
|
||||
if (! empty($context) && $this->pgsqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
} elseif ($this->pgsqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
$dbPrivileges[] = $privilege;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($dbPrivileges)) {
|
||||
if (! empty($dbPrivileges)) {
|
||||
$this->exec(sprintf(
|
||||
'GRANT %s ON DATABASE %s TO %s',
|
||||
join(',', $dbPrivileges),
|
||||
|
@ -529,7 +553,7 @@ class DbTool
|
|||
));
|
||||
}
|
||||
|
||||
if (false === empty($tablePrivileges)) {
|
||||
if (! empty($tablePrivileges)) {
|
||||
foreach ($context as $table) {
|
||||
$this->exec(sprintf(
|
||||
'GRANT %s ON TABLE %s TO %s',
|
||||
|
@ -634,7 +658,7 @@ EOD;
|
|||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach ($mysqlPrivileges as $privilege) {
|
||||
if (false === empty($context) && $this->mysqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
if (! empty($context) && $this->mysqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
}
|
||||
if ($this->mysqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
|
@ -643,25 +667,46 @@ EOD;
|
|||
}
|
||||
|
||||
$dbPrivilegesGranted = true;
|
||||
if (false === empty($dbPrivileges)) {
|
||||
$query = $this->query(
|
||||
'SELECT COUNT(*) as matches'
|
||||
$tablePrivilegesGranted = true;
|
||||
|
||||
if (! empty($dbPrivileges)) {
|
||||
$queryString = 'SELECT COUNT(*) as matches'
|
||||
. ' FROM information_schema.schema_privileges'
|
||||
. ' WHERE grantee = :grantee'
|
||||
. ' AND table_schema = :dbname'
|
||||
. ' AND privilege_type IN (' . join(',', array_map(array($this, 'quote'), $dbPrivileges)) . ')'
|
||||
. ($requireGrants ? " AND is_grantable = 'YES'" : ''),
|
||||
array(':grantee' => $grantee, ':dbname' => $this->config['dbname'])
|
||||
. ' AND privilege_type IN (%s)'
|
||||
. ($requireGrants ? " AND is_grantable = 'YES'" : '');
|
||||
|
||||
$dbAndTableQuery = $this->query(
|
||||
sprintf($queryString, join(',', array_map(array($this, 'quote'), $dbPrivileges))),
|
||||
array(':grantee' => $grantee, ':dbname' => $this->escapeTableWildcards($this->config['dbname']))
|
||||
);
|
||||
$dbPrivilegesGranted = (int) $query->fetchObject()->matches === count($dbPrivileges);
|
||||
$grantedDbAndTablePrivileges = (int) $dbAndTableQuery->fetchObject()->matches;
|
||||
if ($grantedDbAndTablePrivileges === count($dbPrivileges)) {
|
||||
$tableExclusivePrivileges = array_diff($tablePrivileges, $dbPrivileges);
|
||||
if (! empty($tableExclusivePrivileges)) {
|
||||
$tablePrivileges = $tableExclusivePrivileges;
|
||||
$tablePrivilegesGranted = false;
|
||||
}
|
||||
} else {
|
||||
$tablePrivilegesGranted = false;
|
||||
$dbExclusivePrivileges = array_diff($dbPrivileges, $tablePrivileges);
|
||||
if (! empty($dbExclusivePrivileges)) {
|
||||
$dbExclusiveQuery = $this->query(
|
||||
sprintf($queryString, join(',', array_map(array($this, 'quote'), $dbExclusivePrivileges))),
|
||||
array(
|
||||
':grantee' => $grantee,
|
||||
':dbname' => $this->escapeTableWildcards($this->config['dbname'])
|
||||
)
|
||||
);
|
||||
$dbPrivilegesGranted = (int) $dbExclusiveQuery->fetchObject()->matches === count(
|
||||
$dbExclusivePrivileges
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tablePrivilegesGranted = true;
|
||||
if (
|
||||
false === empty($tablePrivileges) && (
|
||||
!$dbPrivilegesGranted || array_intersect($dbPrivileges, $tablePrivileges) != $tablePrivileges
|
||||
)
|
||||
) {
|
||||
if (! $tablePrivilegesGranted && !empty($tablePrivileges)) {
|
||||
$query = $this->query(
|
||||
'SELECT COUNT(*) as matches'
|
||||
. ' FROM information_schema.table_privileges'
|
||||
|
@ -715,7 +760,7 @@ EOD;
|
|||
$dbPrivileges = array();
|
||||
$tablePrivileges = array();
|
||||
foreach (array_intersect($privileges, array_keys($this->pgsqlGrantContexts)) as $privilege) {
|
||||
if (false === empty($context) && $this->pgsqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
if (! empty($context) && $this->pgsqlGrantContexts[$privilege] & static::TABLE_LEVEL) {
|
||||
$tablePrivileges[] = $privilege;
|
||||
}
|
||||
if ($this->pgsqlGrantContexts[$privilege] & static::DATABASE_LEVEL) {
|
||||
|
@ -723,7 +768,8 @@ EOD;
|
|||
}
|
||||
}
|
||||
|
||||
if (false === empty($dbPrivileges)) {
|
||||
if (! empty($dbPrivileges)) {
|
||||
$dbExclusivesGranted = true;
|
||||
foreach ($dbPrivileges as $dbPrivilege) {
|
||||
$query = $this->query(
|
||||
'SELECT has_database_privilege(:user, :dbname, :privilege) AS db_privilege_granted',
|
||||
|
@ -733,11 +779,23 @@ EOD;
|
|||
':privilege' => $dbPrivilege . ($requireGrants ? ' WITH GRANT OPTION' : '')
|
||||
)
|
||||
);
|
||||
$privilegesGranted &= $query->fetchObject()->db_privilege_granted;
|
||||
if (! $query->fetchObject()->db_privilege_granted) {
|
||||
$privilegesGranted = false;
|
||||
if (! in_array($dbPrivilege, $tablePrivileges)) {
|
||||
$dbExclusivesGranted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($privilegesGranted) {
|
||||
// Do not check privileges twice if they are already granted at database level
|
||||
$tablePrivileges = array_diff($tablePrivileges, $dbPrivileges);
|
||||
} elseif ($dbExclusivesGranted) {
|
||||
$privilegesGranted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === empty($tablePrivileges)) {
|
||||
if ($privilegesGranted && !empty($tablePrivileges)) {
|
||||
foreach (array_intersect($context, $this->listTables()) as $table) {
|
||||
foreach ($tablePrivileges as $tablePrivilege) {
|
||||
$query = $this->query(
|
||||
|
|
|
@ -53,13 +53,13 @@ class EnableModuleStep extends Step
|
|||
$okMessage = mt('setup', 'Module "%s" has been successfully enabled.');
|
||||
$failMessage = mt('setup', 'Module "%s" could not be enabled. An error occured:');
|
||||
|
||||
$report = '';
|
||||
$report = array();
|
||||
foreach ($this->moduleNames as $moduleName) {
|
||||
if (isset($this->errors[$moduleName])) {
|
||||
$report .= '<p class="error">' . sprintf($failMessage, $moduleName) . '</p>'
|
||||
. '<p>' . $this->errors[$moduleName]->getMessage() . '</p>';
|
||||
$report[] = sprintf($failMessage, $moduleName);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->errors[$moduleName]->getMessage());
|
||||
} else {
|
||||
$report .= '<p>' . sprintf($okMessage, $moduleName) . '</p>';
|
||||
$report[] = sprintf($okMessage, $moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,14 +53,14 @@ class MakeDirStep extends Step
|
|||
$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 = '';
|
||||
$report = array();
|
||||
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>';
|
||||
$report[] = sprintf($failMessage, basename($path), dirname($path));
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->errors[$path]['message']);
|
||||
} else {
|
||||
$report .= '<p>' . sprintf($okMessage, basename($path), dirname($path)) . '</p>';
|
||||
$report[] = sprintf($okMessage, basename($path), dirname($path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use Icinga\Module\Setup\Forms\ModulePage;
|
|||
use Icinga\Module\Setup\Forms\WelcomePage;
|
||||
use Icinga\Module\Setup\Forms\SummaryPage;
|
||||
use Icinga\Module\Setup\Forms\DbResourcePage;
|
||||
use Icinga\Module\Setup\Forms\PreferencesPage;
|
||||
use Icinga\Module\Setup\Forms\AuthBackendPage;
|
||||
use Icinga\Module\Setup\Forms\AdminAccountPage;
|
||||
use Icinga\Module\Setup\Forms\LdapDiscoveryPage;
|
||||
|
@ -91,7 +90,7 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
);
|
||||
|
||||
/**
|
||||
* @see Wizard::init()
|
||||
* Register all pages and module wizards for this wizard
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
|
@ -99,15 +98,16 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
$this->addPage(new ModulePage());
|
||||
$this->addPage(new RequirementsPage());
|
||||
$this->addPage(new AuthenticationPage());
|
||||
$this->addPage(new PreferencesPage());
|
||||
$this->addPage(new DbResourcePage());
|
||||
$this->addPage(new DbResourcePage(array('name' => 'setup_auth_db_resource')));
|
||||
$this->addPage(new DatabaseCreationPage(array('name' => 'setup_auth_db_creation')));
|
||||
$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 DbResourcePage(array('name' => 'setup_config_db_resource')));
|
||||
$this->addPage(new DatabaseCreationPage(array('name' => 'setup_config_db_creation')));
|
||||
$this->addPage(new SummaryPage(array('name' => 'setup_summary')));
|
||||
|
||||
if (($modulePageData = $this->getPageData('setup_modules')) !== null) {
|
||||
|
@ -119,25 +119,19 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::setupPage()
|
||||
* Setup the given page that is either going to be displayed or validated
|
||||
*
|
||||
* @param Form $page The page to setup
|
||||
* @param Request $request The current request
|
||||
*/
|
||||
public function setupPage(Form $page, Request $request)
|
||||
{
|
||||
if ($page->getName() === 'setup_requirements') {
|
||||
$page->setWizard($this);
|
||||
} elseif ($page->getName() === 'setup_preferences_type') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->create()->getElement('store')->setValue('db');
|
||||
$page->addDescription(mt(
|
||||
'setup',
|
||||
'Note that choosing "Database" causes Icinga Web 2 to use the same database as for authentication.'
|
||||
));
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_authentication_backend') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->setResourceConfig($this->getPageData('setup_db_resource'));
|
||||
$page->setResourceConfig($this->getPageData('setup_auth_db_resource'));
|
||||
} elseif ($authData['type'] === 'ldap') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
|
||||
|
||||
|
@ -152,38 +146,46 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
$page->setBackendConfig($this->getPageData('setup_authentication_backend'));
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->setResourceConfig($this->getPageData('setup_db_resource'));
|
||||
$page->setResourceConfig($this->getPageData('setup_auth_db_resource'));
|
||||
} elseif ($authData['type'] === 'ldap') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_database_creation') {
|
||||
} elseif ($page->getName() === 'setup_auth_db_creation' || $page->getName() === 'setup_config_db_creation') {
|
||||
$page->setDatabaseSetupPrivileges(
|
||||
array_unique(array_merge($this->databaseCreationPrivileges, $this->databaseSetupPrivileges))
|
||||
);
|
||||
$page->setDatabaseUsagePrivileges($this->databaseUsagePrivileges);
|
||||
$page->setResourceConfig($this->getPageData('setup_db_resource'));
|
||||
$page->setResourceConfig(
|
||||
$this->getPageData('setup_auth_db_resource') ?: $this->getPageData('setup_config_db_resource')
|
||||
);
|
||||
} elseif ($page->getName() === 'setup_summary') {
|
||||
$page->setSubjectTitle('Icinga Web 2');
|
||||
$page->setSummary($this->getSetup()->getSummary());
|
||||
} elseif ($page->getName() === 'setup_db_resource') {
|
||||
} elseif ($page->getName() === 'setup_config_db_resource') {
|
||||
$ldapData = $this->getPageData('setup_ldap_resource');
|
||||
if ($ldapData !== null && $request->getPost('name') === $ldapData['name']) {
|
||||
$page->addError(
|
||||
$page->error(
|
||||
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');
|
||||
if (isset($suggestion['resource'])) {
|
||||
$page->populate($suggestion['resource']);
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_general_config') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->create()->getElement('global_config_backend')->setValue('db');
|
||||
$page->info(
|
||||
mt(
|
||||
'setup',
|
||||
'Note that choosing "Database" as preference storage causes'
|
||||
. ' Icinga Web 2 to use the same database as for authentication.'
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
} elseif ($page->getName() === 'setup_authentication_type' && $this->getDirection() === static::FORWARD) {
|
||||
$authData = $this->getPageData($page->getName());
|
||||
if ($authData !== null && $request->getPost('type') !== $authData['type']) {
|
||||
|
@ -192,21 +194,37 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
$pageData = & $this->getPageData();
|
||||
unset($pageData['setup_admin_account']);
|
||||
unset($pageData['setup_authentication_backend']);
|
||||
|
||||
if ($authData['type'] === 'db') {
|
||||
unset($pageData['setup_auth_db_resource']);
|
||||
unset($pageData['setup_auth_db_creation']);
|
||||
} elseif ($request->getPost('type') === 'db') {
|
||||
unset($pageData['setup_config_db_resource']);
|
||||
unset($pageData['setup_config_db_creation']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::getNewPage()
|
||||
* Return the new page to set as current page
|
||||
*
|
||||
* {@inheritdoc} Runs additional checks related to some registered pages.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$skip = false;
|
||||
$newPage = parent::getNewPage($requestedPage, $originPage);
|
||||
if ($newPage->getName() === 'setup_db_resource') {
|
||||
$prefData = $this->getPageData('setup_preferences_type');
|
||||
if ($newPage->getName() === 'setup_auth_db_resource') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
$skip = $prefData['store'] !== 'db' && $authData['type'] !== 'db';
|
||||
$skip = $authData['type'] !== 'db';
|
||||
} elseif ($newPage->getname() === 'setup_ldap_discovery') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
$skip = $authData['type'] !== 'ldap';
|
||||
|
@ -215,13 +233,22 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
} 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']) {
|
||||
} elseif ($newPage->getName() === 'setup_config_db_resource') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
$configData = $this->getPageData('setup_general_config');
|
||||
$skip = $authData['type'] === 'db' || $configData['global_config_backend'] !== 'db';
|
||||
} elseif (in_array($newPage->getName(), array('setup_auth_db_creation', 'setup_config_db_creation'))) {
|
||||
if (
|
||||
($newPage->getName() === 'setup_auth_db_creation' || $this->hasPageData('setup_config_db_resource'))
|
||||
&& (($config = $this->getPageData('setup_auth_db_resource')) !== null
|
||||
|| ($config = $this->getPageData('setup_config_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) {
|
||||
if (array_search(reset($this->databaseTables), $db->listTables()) === false) {
|
||||
// In case the database schema does not yet exist the
|
||||
// user needs the privileges to setup the database
|
||||
$skip = $db->checkPrivileges($this->databaseSetupPrivileges, $this->databaseTables);
|
||||
|
@ -254,7 +281,9 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::addButtons()
|
||||
* 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)
|
||||
{
|
||||
|
@ -270,7 +299,7 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see Wizard::clearSession()
|
||||
* Clear the session being used by this wizard and drop the setup token
|
||||
*/
|
||||
public function clearSession()
|
||||
{
|
||||
|
@ -283,29 +312,54 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getSetup()
|
||||
* Return the setup for this wizard
|
||||
*
|
||||
* @return Setup
|
||||
*/
|
||||
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']
|
||||
if (
|
||||
isset($pageData['setup_auth_db_resource'])
|
||||
&& !$pageData['setup_auth_db_resource']['skip_validation']
|
||||
&& (! isset($pageData['setup_auth_db_creation'])
|
||||
|| !$pageData['setup_auth_db_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']
|
||||
'resourceConfig' => $pageData['setup_auth_db_resource'],
|
||||
'adminName' => isset($pageData['setup_auth_db_creation']['username'])
|
||||
? $pageData['setup_auth_db_creation']['username']
|
||||
: null,
|
||||
'adminPassword' => isset($pageData['setup_database_creation']['password'])
|
||||
? $pageData['setup_database_creation']['password']
|
||||
'adminPassword' => isset($pageData['setup_auth_db_creation']['password'])
|
||||
? $pageData['setup_auth_db_creation']['password']
|
||||
: null,
|
||||
'schemaPath' => Config::module('setup')
|
||||
->get('schema', 'path', Icinga::app()->getBaseDir('etc' . DIRECTORY_SEPARATOR . 'schema'))
|
||||
))
|
||||
);
|
||||
} elseif (
|
||||
isset($pageData['setup_config_db_resource'])
|
||||
&& !$pageData['setup_config_db_resource']['skip_validation']
|
||||
&& (! isset($pageData['setup_config_db_creation'])
|
||||
|| !$pageData['setup_config_db_creation']['skip_validation']
|
||||
)
|
||||
) {
|
||||
$setup->addStep(
|
||||
new DatabaseStep(array(
|
||||
'tables' => $this->databaseTables,
|
||||
'privileges' => $this->databaseUsagePrivileges,
|
||||
'resourceConfig' => $pageData['setup_config_db_resource'],
|
||||
'adminName' => isset($pageData['setup_config_db_creation']['username'])
|
||||
? $pageData['setup_config_db_creation']['username']
|
||||
: null,
|
||||
'adminPassword' => isset($pageData['setup_config_db_creation']['password'])
|
||||
? $pageData['setup_config_db_creation']['password']
|
||||
: null,
|
||||
'schemaPath' => Config::module('setup')
|
||||
->get('schema', 'path', Icinga::app()->getBaseDir('etc' . DIRECTORY_SEPARATOR . 'schema'))
|
||||
|
@ -315,22 +369,24 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
|
||||
$setup->addStep(
|
||||
new GeneralConfigStep(array(
|
||||
'generalConfig' => $pageData['setup_general_config'],
|
||||
'preferencesStore' => $pageData['setup_preferences_type']['store'],
|
||||
'preferencesResource' => isset($pageData['setup_db_resource']['name'])
|
||||
? $pageData['setup_db_resource']['name']
|
||||
: null
|
||||
'generalConfig' => $pageData['setup_general_config'],
|
||||
'resourceName' => isset($pageData['setup_auth_db_resource']['name'])
|
||||
? $pageData['setup_auth_db_resource']['name']
|
||||
: (isset($pageData['setup_config_db_resource']['name'])
|
||||
? $pageData['setup_config_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']
|
||||
if ($adminAccountType === 'new_user' && !$pageData['setup_auth_db_resource']['skip_validation']
|
||||
&& (! isset($pageData['setup_auth_db_creation'])
|
||||
|| !$pageData['setup_auth_db_creation']['skip_validation']
|
||||
)
|
||||
) {
|
||||
$adminAccountData['resourceConfig'] = $pageData['setup_db_resource'];
|
||||
$adminAccountData['resourceConfig'] = $pageData['setup_auth_db_resource'];
|
||||
$adminAccountData['password'] = $pageData['setup_admin_account']['new_user_password'];
|
||||
}
|
||||
$authType = $pageData['setup_authentication_type']['type'];
|
||||
|
@ -338,18 +394,25 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
new AuthenticationStep(array(
|
||||
'adminAccountData' => $adminAccountData,
|
||||
'backendConfig' => $pageData['setup_authentication_backend'],
|
||||
'resourceName' => $authType === 'db' ? $pageData['setup_db_resource']['name'] : (
|
||||
'resourceName' => $authType === 'db' ? $pageData['setup_auth_db_resource']['name'] : (
|
||||
$authType === 'ldap' ? $pageData['setup_ldap_resource']['name'] : null
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
if (isset($pageData['setup_db_resource']) || isset($pageData['setup_ldap_resource'])) {
|
||||
if (
|
||||
isset($pageData['setup_auth_db_resource'])
|
||||
|| isset($pageData['setup_config_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,
|
||||
'dbResourceConfig' => isset($pageData['setup_auth_db_resource'])
|
||||
? array_diff_key($pageData['setup_auth_db_resource'], array('skip_validation' => null))
|
||||
: (isset($pageData['setup_config_db_resource'])
|
||||
? array_diff_key($pageData['setup_config_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
|
||||
|
@ -369,7 +432,9 @@ class WebWizard extends Wizard implements SetupWizard
|
|||
}
|
||||
|
||||
/**
|
||||
* @see SetupWizard::getRequirements()
|
||||
* Return the requirements of this wizard
|
||||
*
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function getRequirements($skipModules = false)
|
||||
{
|
||||
|
|
|
@ -268,4 +268,4 @@ form ul.hints {
|
|||
font-size: 0.8em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -389,6 +389,14 @@ html {
|
|||
width: 1px;
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
content: ".";
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.multi-commands {
|
||||
padding-top: 0em;
|
||||
font-size: 0.9em;
|
||||
|
|
|
@ -143,6 +143,7 @@
|
|||
&:hover, &:focus, &:active {
|
||||
background-color: #666;
|
||||
border-color: #666;
|
||||
cursor: pointer;
|
||||
|
||||
&[disabled="1"] {
|
||||
background-color: #aaa;
|
||||
|
@ -182,43 +183,51 @@ form#setup_requirements {
|
|||
}
|
||||
}
|
||||
|
||||
#setup > table.requirements {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
#setup ul.requirements {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
#setup table.requirements {
|
||||
margin: -1em;
|
||||
border-spacing: 1em;
|
||||
border-collapse: separate;
|
||||
li {
|
||||
margin-bottom: 1em;
|
||||
|
||||
td {
|
||||
padding: 0;
|
||||
|
||||
h2 {
|
||||
margin: 0 1em 0 0;
|
||||
font-variant: normal;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 102%; // Just a hack for webkit, remove this in case you can't see any difference or make it work without it
|
||||
}
|
||||
|
||||
ul {
|
||||
& > ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
list-style-type: square;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
&.title {
|
||||
div {
|
||||
float: left;
|
||||
padding-top: 0.4em;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
}
|
||||
|
||||
div.title {
|
||||
width: 25%;
|
||||
|
||||
h3 {
|
||||
padding: 0;
|
||||
margin: 0 1em 0 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.desc {
|
||||
div.description {
|
||||
width: 50%;
|
||||
border-left: 0.4em solid transparent;
|
||||
border-right: 0.4em solid transparent;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
list-style-type: square;
|
||||
}
|
||||
}
|
||||
|
||||
&.state {
|
||||
div.state {
|
||||
width: 25%;
|
||||
color: white;
|
||||
padding: 0.4em;
|
||||
|
@ -300,50 +309,35 @@ form#setup_requirements {
|
|||
}
|
||||
}
|
||||
|
||||
.conspicuous-state-notification {
|
||||
width: 66%;
|
||||
margin: 0 auto;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#setup-finish {
|
||||
div.report {
|
||||
padding: 1em;
|
||||
border: 1px solid lightgrey;
|
||||
border-radius: 0em;
|
||||
h2 {
|
||||
padding: 0.5em;
|
||||
border-bottom: 0;
|
||||
font-variant: normal;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
|
||||
div.line-separator {
|
||||
width: 50%;
|
||||
height: 1px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
&.success {
|
||||
background-color: @colorOk;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em;
|
||||
color: #444;
|
||||
text-align: center;
|
||||
|
||||
&.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
&.failure {
|
||||
.conspicuous-state-notification;
|
||||
background-color: @colorCritical;
|
||||
}
|
||||
|
||||
&.success {
|
||||
.conspicuous-state-notification;
|
||||
background-color: @colorOk;
|
||||
}
|
||||
&.failure {
|
||||
background-color: @colorCritical;
|
||||
}
|
||||
}
|
||||
|
||||
textarea.report {
|
||||
width: 66%;
|
||||
height: 25em;
|
||||
}
|
||||
|
||||
div.buttons {
|
||||
margin-top: 0.5em;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
/**
|
||||
* Icinga.Behavior.ActionTable
|
||||
*
|
||||
* A multi selection that distincts between the table rows using the row action URL filter
|
||||
*/
|
||||
(function(Icinga, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Remove one leading and trailing bracket and all text outside those brackets
|
||||
*
|
||||
* @param str {String}
|
||||
* @returns {string}
|
||||
*/
|
||||
var stripBrackets = function (str) {
|
||||
return str.replace(/^[^\(]*\(/, '').replace(/\)[^\)]*$/, '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the filter query contained in the given url filter string
|
||||
*
|
||||
* @param filterString {String}
|
||||
*
|
||||
* @returns {Array} An object containing each row filter
|
||||
*/
|
||||
var parseSelectionQuery = function(filterString) {
|
||||
var selections = [];
|
||||
$.each(stripBrackets(filterString).split('|'), function(i, row) {
|
||||
var tuple = {};
|
||||
$.each(stripBrackets(row).split('&'), function(i, keyValue) {
|
||||
var s = keyValue.split('=');
|
||||
tuple[s[0]] = decodeURIComponent(s[1]);
|
||||
});
|
||||
selections.push(tuple);
|
||||
});
|
||||
return selections;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the selection of an action table
|
||||
*
|
||||
* @param table {HTMLElement} The table
|
||||
* @param {Icinga}
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var Selection = function(table, icinga) {
|
||||
this.$el = $(table);
|
||||
this.icinga = icinga;
|
||||
|
||||
if (this.hasMultiselection()) {
|
||||
if (! this.getMultiselectionKeys().length) {
|
||||
icinga.logger.error('multiselect table has no data-icinga-multiselect-data');
|
||||
}
|
||||
if (! this.getMultiselectionUrl()) {
|
||||
icinga.logger.error('multiselect table has no data-icinga-multiselect-url');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Selection.prototype = {
|
||||
|
||||
/**
|
||||
* Return all rows as jQuery selector
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
rows: function() {
|
||||
return this.$el.find('tr');
|
||||
},
|
||||
|
||||
/**
|
||||
* Return all row action links as jQuery selector
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
rowActions: function() {
|
||||
return this.$el.find('tr a.rowaction');
|
||||
},
|
||||
|
||||
/**
|
||||
* Return all selected rows as jQuery selector
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
selections: function() {
|
||||
return this.$el.find('tr.active');
|
||||
},
|
||||
|
||||
/**
|
||||
* If this selection allows selecting multiple rows
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasMultiselection: function() {
|
||||
return this.$el.hasClass('multiselect');
|
||||
},
|
||||
|
||||
/**
|
||||
* Return all filter keys that are significant when applying the selection
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
getMultiselectionKeys: function() {
|
||||
var data = this.$el.data('icinga-multiselect-data');
|
||||
return (data && data.split(',')) || [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the target URL that is used when multi selecting rows
|
||||
*
|
||||
* This URL may differ from the url that is used when applying single rows
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
getMultiselectionUrl: function() {
|
||||
return this.$el.data('icinga-multiselect-url');
|
||||
},
|
||||
|
||||
/**
|
||||
* Read all filter data from the given row
|
||||
*
|
||||
* @param row {jQuery} The row element
|
||||
*
|
||||
* @returns {Object} An object containing all filter data in this row as key-value pairs
|
||||
*/
|
||||
getRowData: function(row) {
|
||||
var params = this.icinga.utils.parseUrl(row.attr('href')).params;
|
||||
var tuple = {};
|
||||
var keys = this.getMultiselectionKeys();
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
if (params[key]) {
|
||||
tuple[key] = params[key];
|
||||
}
|
||||
}
|
||||
return tuple;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deselect all selected rows
|
||||
*/
|
||||
clear: function() {
|
||||
this.selections().removeClass('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add all rows that match the given filter to the selection
|
||||
*
|
||||
* @param filter {jQuery|Object} Either an object containing filter variables or the actual row to select
|
||||
*/
|
||||
select: function(filter) {
|
||||
if (filter instanceof jQuery) {
|
||||
filter.addClass('active');
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
var url = this.getMultiselectionUrl();
|
||||
this.rowActions()
|
||||
.filter(
|
||||
function (i, el) {
|
||||
var params = self.getRowData($(el));
|
||||
if (self.icinga.utils.objectKeys(params).length !== self.icinga.utils.objectKeys(filter).length) {
|
||||
return false;
|
||||
}
|
||||
var equal = true;
|
||||
$.each(params, function(key, value) {
|
||||
if (filter[key] !== value) {
|
||||
equal = false;
|
||||
}
|
||||
});
|
||||
return equal;
|
||||
}
|
||||
)
|
||||
.closest('tr')
|
||||
.addClass('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the selection of the row between on and off
|
||||
*
|
||||
* @param row {jQuery} The row to toggle
|
||||
*/
|
||||
toggle: function(row) {
|
||||
row.toggleClass('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new selection range to the closest table, using the selected row as
|
||||
* range target.
|
||||
*
|
||||
* @param row {jQuery} The target of the selected range.
|
||||
*
|
||||
* @returns {boolean} If the selection was changed.
|
||||
*/
|
||||
range: function(row) {
|
||||
var from, to;
|
||||
var selected = row.first().get(0);
|
||||
this.rows().each(function(i, el) {
|
||||
if ($(el).hasClass('active') || el === selected) {
|
||||
if (!from) {
|
||||
from = el;
|
||||
}
|
||||
to = el;
|
||||
}
|
||||
});
|
||||
var inRange = false;
|
||||
this.rows().each(function(i, el) {
|
||||
if (el === from) {
|
||||
inRange = true;
|
||||
}
|
||||
if (inRange) {
|
||||
$(el).addClass('active');
|
||||
}
|
||||
if (el === to) {
|
||||
inRange = false;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Select rows that target the given url
|
||||
*
|
||||
* @param url {String} The target url
|
||||
*/
|
||||
selectUrl: function(url) {
|
||||
this.rows().filter('[href="' + url + '"]').addClass('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert all currently selected rows into an url query string
|
||||
*
|
||||
* @returns {String} The filter string
|
||||
*/
|
||||
toQuery: function() {
|
||||
var self = this;
|
||||
var selections = this.selections();
|
||||
var queries = [];
|
||||
if (selections.length === 1) {
|
||||
return $(selections[0]).attr('href');
|
||||
} else if (selections.length > 1 && self.hasMultiselection()) {
|
||||
selections.each(function (i, el) {
|
||||
var parts = [];
|
||||
$.each(self.getRowData($(el)), function(key, value) {
|
||||
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
|
||||
});
|
||||
queries.push('(' + parts.join('&') + ')');
|
||||
});
|
||||
return self.getMultiselectionUrl() + '?(' + queries.join('|') + ')';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the displayed active columns using the current page location
|
||||
*/
|
||||
refresh: function() {
|
||||
this.clear();
|
||||
var hash = this.icinga.utils.parseUrl(window.location.href).hash;
|
||||
if (this.hasMultiselection()) {
|
||||
var query = parseSelectionQuery(hash);
|
||||
if (query.length > 1 && this.getMultiselectionUrl() === this.icinga.utils.parseUrl(hash.substr(1)).path) {
|
||||
// select all rows with matching filters
|
||||
var self = this;
|
||||
$.each(query, function(i, selection) {
|
||||
self.select(selection);
|
||||
});
|
||||
}
|
||||
if (query.length > 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.selectUrl(hash.substr(1));
|
||||
}
|
||||
};
|
||||
|
||||
Icinga.Behaviors = Icinga.Behaviors || {};
|
||||
|
||||
var ActionTable = function (icinga) {
|
||||
Icinga.EventListener.call(this, icinga);
|
||||
|
||||
/**
|
||||
* The hash that is currently being loaded
|
||||
*
|
||||
* @var String
|
||||
*/
|
||||
this.loadingHash = null;
|
||||
|
||||
/**
|
||||
* If currently loading
|
||||
*
|
||||
* @var Boolean
|
||||
*/
|
||||
this.loading = false;
|
||||
|
||||
this.on('rendered', this.onRendered, this);
|
||||
this.on('click', 'table.action tr[href]', this.onRowClicked, this);
|
||||
};
|
||||
ActionTable.prototype = new Icinga.EventListener();
|
||||
|
||||
/**
|
||||
* Return all active tables in this table, or in the context as jQuery selector
|
||||
*
|
||||
* @param context {HTMLElement}
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
ActionTable.prototype.tables = function(context) {
|
||||
if (context) {
|
||||
return $(context).find('table.action');
|
||||
}
|
||||
return $('table.action');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle clicks on table rows and update selection and history
|
||||
*/
|
||||
ActionTable.prototype.onRowClicked = function (event) {
|
||||
var self = event.data.self;
|
||||
var $tr = $(event.target).closest('tr');
|
||||
var table = new Selection($tr.closest('table.action')[0], self.icinga);
|
||||
|
||||
// allow form actions in table rows to pass through
|
||||
if ($(event.target).closest('form').length) {
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
// update selection
|
||||
if (table.hasMultiselection()) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
// add to selection
|
||||
table.toggle($tr);
|
||||
} else if (event.shiftKey) {
|
||||
// range selection
|
||||
table.range($tr);
|
||||
} else {
|
||||
// single selection
|
||||
table.clear();
|
||||
table.select($tr);
|
||||
}
|
||||
} else {
|
||||
table.clear();
|
||||
table.select($tr);
|
||||
}
|
||||
|
||||
// update history
|
||||
var url = self.icinga.utils.parseUrl(window.location.href.split('#')[0]);
|
||||
var count = table.selections().length;
|
||||
var state = url.path + url.query;
|
||||
if (count > 0) {
|
||||
var query = table.toQuery();
|
||||
self.icinga.loader.loadUrl(query, self.icinga.events.getLinkTargetFor($tr));
|
||||
state += '#!' + query;
|
||||
} else {
|
||||
if (self.icinga.events.getLinkTargetFor($tr).attr('id') === 'col2') {
|
||||
self.icinga.ui.layout1col();
|
||||
}
|
||||
}
|
||||
self.icinga.history.pushUrl(state);
|
||||
|
||||
// re draw all table selections
|
||||
self.tables().each(function () {
|
||||
new Selection(this, self.icinga).refresh();
|
||||
});
|
||||
|
||||
// update selection info
|
||||
$('.selection-info-count').text(table.selections().size());
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that
|
||||
*/
|
||||
ActionTable.prototype.onRendered = function(evt) {
|
||||
var container = evt.target;
|
||||
var self = evt.data.self;
|
||||
|
||||
// draw all selections
|
||||
self.tables().each(function(i, el) {
|
||||
new Selection(el, self.icinga).refresh();
|
||||
});
|
||||
|
||||
// update displayed selection count
|
||||
var table = new Selection(self.tables(container).first());
|
||||
$(container).find('.selection-info-count').text(table.selections().size());
|
||||
};
|
||||
|
||||
Icinga.Behaviors.ActionTable = ActionTable;
|
||||
|
||||
}) (Icinga, jQuery);
|
|
@ -117,9 +117,6 @@
|
|||
$(document).on('click', 'a', { self: this }, this.linkClicked);
|
||||
$(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
|
||||
|
||||
// Select a table row
|
||||
$(document).on('click', 'table.multiselect tr[href]', { self: this }, this.rowSelected);
|
||||
|
||||
// We catch all form submit events
|
||||
$(document).on('submit', 'form', { self: this }, this.submitForm);
|
||||
|
||||
|
@ -303,74 +300,6 @@
|
|||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle table selection.
|
||||
*/
|
||||
rowSelected: function(event) {
|
||||
var self = event.data.self;
|
||||
var icinga = self.icinga;
|
||||
var $tr = $(this);
|
||||
var $table = $tr.closest('table.multiselect');
|
||||
var data = self.icinga.ui.getSelectionKeys($table);
|
||||
var url = $table.data('icinga-multiselect-url');
|
||||
|
||||
if ($(event.target).closest('form').length) {
|
||||
// allow form actions in table rows to pass through
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (!data) {
|
||||
icinga.logger.error('multiselect table has no data-icinga-multiselect-data');
|
||||
return;
|
||||
}
|
||||
if (!url) {
|
||||
icinga.logger.error('multiselect table has no data-icinga-multiselect-url');
|
||||
return;
|
||||
}
|
||||
|
||||
// update selection
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
icinga.ui.toogleTableRowSelection($tr);
|
||||
// multi selection
|
||||
} else if (event.shiftKey) {
|
||||
// range selection
|
||||
icinga.ui.addTableRowRangeSelection($tr);
|
||||
} else {
|
||||
// single selection
|
||||
icinga.ui.setTableRowSelection($tr);
|
||||
}
|
||||
// focus only the current table.
|
||||
icinga.ui.focusTable($table[0]);
|
||||
|
||||
var $target = self.getLinkTargetFor($tr);
|
||||
|
||||
var $trs = $table.find('tr[href].active');
|
||||
if ($trs.length > 1) {
|
||||
var selectionData = icinga.ui.getSelectionSetData($trs, data);
|
||||
var query = icinga.ui.selectionDataToQuery(selectionData);
|
||||
icinga.loader.loadUrl(url + '?' + query, $target);
|
||||
icinga.ui.storeSelectionData(selectionData);
|
||||
icinga.ui.provideSelectionCount();
|
||||
} else if ($trs.length === 1) {
|
||||
// display a single row
|
||||
$tr = $trs.first();
|
||||
icinga.loader.loadUrl($tr.attr('href'), $target);
|
||||
icinga.ui.storeSelectionData($tr.attr('href'));
|
||||
icinga.ui.provideSelectionCount();
|
||||
} else {
|
||||
// display nothing
|
||||
if ($target.attr('id') === 'col2') {
|
||||
icinga.ui.layout1col();
|
||||
}
|
||||
icinga.ui.storeSelectionData(null);
|
||||
icinga.ui.provideSelectionCount();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle anchor, i.e. focus the element which is referenced by the anchor
|
||||
*
|
||||
|
@ -406,16 +335,16 @@
|
|||
// Special checks for link clicks in multiselect rows
|
||||
if (! $a.is('tr[href]') && $a.closest('tr[href]').length > 0 && $a.closest('table.multiselect').length > 0) {
|
||||
|
||||
// Forward clicks to ANY link with special key pressed to rowSelected
|
||||
// ignoray clicks to ANY link with special key pressed
|
||||
if (event.ctrlKey || event.metaKey || event.shiftKey)
|
||||
{
|
||||
return self.rowSelected.call($a.closest('tr[href]'), event);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forward inner links matching the row URL to rowSelected
|
||||
// ignore inner links matching the row URL
|
||||
if ($a.attr('href') === $a.closest('tr[href]').attr('href'))
|
||||
{
|
||||
return self.rowSelected.call($a.closest('tr[href]'), event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,8 +406,6 @@
|
|||
icinga.ui.layout1col();
|
||||
}
|
||||
$('table tr[href].active').removeClass('active');
|
||||
icinga.ui.storeSelectionData(null);
|
||||
icinga.ui.loadSelectionData();
|
||||
icinga.history.pushCurrentState();
|
||||
}
|
||||
}
|
||||
|
@ -574,8 +501,6 @@
|
|||
$(window).off('beforeunload', this.onUnload);
|
||||
$(document).off('scroll', '.container', this.onContainerScroll);
|
||||
$(document).off('click', 'a', this.linkClicked);
|
||||
$(document).off('click', 'table.action tr[href]', this.rowSelected);
|
||||
$(document).off('click', 'table.action tr a', this.rowSelected);
|
||||
$(document).off('submit', 'form', this.submitForm);
|
||||
$(document).off('change', 'form select.autosubmit', this.submitForm);
|
||||
$(document).off('change', 'form input.autosubmit', this.submitForm);
|
||||
|
|
|
@ -116,7 +116,6 @@
|
|||
req.complete(this.onComplete);
|
||||
req.autorefresh = autorefresh;
|
||||
req.action = action;
|
||||
req.failure = false;
|
||||
req.addToHistory = true;
|
||||
|
||||
if (id) {
|
||||
|
@ -453,6 +452,8 @@
|
|||
var $el = $(el);
|
||||
if ($el.hasClass('dashboard')) {
|
||||
return;
|
||||
} else {
|
||||
|
||||
}
|
||||
var url = $el.data('icingaUrl');
|
||||
targets[i].data('icingaUrl', url);
|
||||
|
@ -504,42 +505,6 @@
|
|||
this.icinga.ui.fixDebugVisibility().triggerWindowResize();
|
||||
}
|
||||
self.cacheLoadedIcons(req.$target);
|
||||
|
||||
if (active) {
|
||||
var focusedUrl = this.icinga.ui.getFocusedContainerDataUrl();
|
||||
var oldSelectionData = this.icinga.ui.loadSelectionData();
|
||||
if (typeof oldSelectionData === 'string') {
|
||||
$('[href="' + oldSelectionData + '"]', req.$target).addClass('active');
|
||||
|
||||
} else if (oldSelectionData !== null) {
|
||||
var $container;
|
||||
if (!focusedUrl) {
|
||||
$container = $('document').first();
|
||||
} else {
|
||||
$container = $('.container[data-icinga-url="' + focusedUrl + '"]');
|
||||
}
|
||||
|
||||
var $table = $container.find('table.action').first();
|
||||
var keys = self.icinga.ui.getSelectionKeys($table);
|
||||
|
||||
// build map of selected queries
|
||||
var oldSelectionQueries = {};
|
||||
$.each(oldSelectionData, function(i, query){
|
||||
oldSelectionQueries[self.icinga.ui.selectionDataToQueryComp(query)] = true;
|
||||
});
|
||||
|
||||
// set all new selections to active
|
||||
$table.find('tr[href]').filter(function(){
|
||||
var $tr = $(this);
|
||||
var rowData = self.icinga.ui.getSelectionData($tr, keys, self.icinga);
|
||||
var newSelectionQuery = self.icinga.ui.selectionDataToQueryComp(rowData);
|
||||
if (oldSelectionQueries[newSelectionQuery]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).addClass('active');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -587,7 +552,7 @@
|
|||
// Update history when necessary. Don't do so for requests triggered
|
||||
// by history or autorefresh events
|
||||
if (! req.autorefresh && req.addToHistory) {
|
||||
if (req.$target.hasClass('container') && ! req.failure) {
|
||||
if (req.$target.hasClass('container')) {
|
||||
// We only want to care about top-level containers
|
||||
if (req.$target.parent().closest('.container').length === 0) {
|
||||
this.icinga.history.pushCurrentState();
|
||||
|
@ -595,7 +560,7 @@
|
|||
} else {
|
||||
// Request wasn't for a container, so it's usually the body
|
||||
// or the full layout. Push request URL to history:
|
||||
this.icinga.history.pushCurrentState();
|
||||
this.icinga.history.pushUrl(req.url);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,10 +588,6 @@
|
|||
onFailure: function (req, textStatus, errorThrown) {
|
||||
var url = req.url;
|
||||
|
||||
req.failure = true;
|
||||
|
||||
req.$target.data('icingaUrl', req.url);
|
||||
|
||||
/*
|
||||
* Test if a manual actions comes in and autorefresh is active: Stop refreshing
|
||||
*/
|
||||
|
|
|
@ -9,13 +9,6 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
// Stores the icinga-data-url of the last focused table.
|
||||
var focusedTableDataUrl = null;
|
||||
|
||||
// The stored selection data, useful for preserving selections over
|
||||
// multiple reload-cycles.
|
||||
var selectionData = null;
|
||||
|
||||
Icinga.UI = function (icinga) {
|
||||
|
||||
this.icinga = icinga;
|
||||
|
@ -38,8 +31,7 @@
|
|||
this.fadeNotificationsAway();
|
||||
},
|
||||
|
||||
fadeNotificationsAway: function()
|
||||
{
|
||||
fadeNotificationsAway: function() {
|
||||
var icinga = this.icinga;
|
||||
$('#notifications li')
|
||||
.not('.fading-out')
|
||||
|
@ -298,226 +290,6 @@
|
|||
return $('#main > .container').length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the given table-row to the selection of the closest
|
||||
* table and deselect all other rows of the closest table.
|
||||
*
|
||||
* @param $tr {jQuery} The selected table row.
|
||||
* @returns {boolean} If the selection was changed.
|
||||
*/
|
||||
setTableRowSelection: function ($tr) {
|
||||
var $table = $tr.closest('table.multiselect');
|
||||
$table.find('tr[href].active').removeClass('active');
|
||||
$tr.addClass('active');
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the given table row to "on" when not selected, or to "off" when
|
||||
* currently selected.
|
||||
*
|
||||
* @param $tr {jQuery} The table row.
|
||||
* @returns {boolean} If the selection was changed.
|
||||
*/
|
||||
toogleTableRowSelection: function ($tr) {
|
||||
// multi selection
|
||||
if ($tr.hasClass('active')) {
|
||||
$tr.removeClass('active');
|
||||
} else {
|
||||
$tr.addClass('active');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new selection range to the closest table, using the selected row as
|
||||
* range target.
|
||||
*
|
||||
* @param $tr {jQuery} The target of the selected range.
|
||||
* @returns {boolean} If the selection was changed.
|
||||
*/
|
||||
addTableRowRangeSelection: function ($tr) {
|
||||
var $table = $tr.closest('table.multiselect');
|
||||
var $rows = $table.find('tr[href]'),
|
||||
from, to;
|
||||
var selected = $tr.first().get(0);
|
||||
$rows.each(function(i, el) {
|
||||
if ($(el).hasClass('active') || el === selected) {
|
||||
if (!from) {
|
||||
from = el;
|
||||
}
|
||||
to = el;
|
||||
}
|
||||
});
|
||||
var inRange = false;
|
||||
$rows.each(function(i, el){
|
||||
if (el === from) {
|
||||
inRange = true;
|
||||
}
|
||||
if (inRange) {
|
||||
$(el).addClass('active');
|
||||
}
|
||||
if (el === to) {
|
||||
inRange = false;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Read the data from a whole set of selections.
|
||||
*
|
||||
* @param $selections {jQuery} All selected rows in a jQuery-selector.
|
||||
* @param keys {Array} An array containing all valid keys.
|
||||
* @returns {Array} An array containing an object with the data for each selection.
|
||||
*/
|
||||
getSelectionSetData: function($selections, keys) {
|
||||
var selections = [];
|
||||
var icinga = this.icinga;
|
||||
|
||||
// read all current selections
|
||||
$selections.each(function(ind, selected) {
|
||||
selections.push(icinga.ui.getSelectionData($(selected), keys, icinga));
|
||||
});
|
||||
return selections;
|
||||
},
|
||||
|
||||
getSelectionKeys: function($selection)
|
||||
{
|
||||
var d = $selection.data('icinga-multiselect-data') && $selection.data('icinga-multiselect-data').split(',');
|
||||
return d || [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Read the data from the given selected object.
|
||||
*
|
||||
* @param $selection {jQuery} The selected object.
|
||||
* @param keys {Array} An array containing all valid keys.
|
||||
* @param icinga {Icinga} The main icinga object.
|
||||
* @returns {Object} An object containing all key-value pairs associated with this selection.
|
||||
*/
|
||||
getSelectionData: function($selection, keys, icinga)
|
||||
{
|
||||
var url = $selection.attr('href');
|
||||
var params = this.icinga.utils.parseUrl(url).params;
|
||||
var tuple = {};
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
if (params[key]) {
|
||||
tuple[key] = params[key];
|
||||
}
|
||||
}
|
||||
return tuple;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a set of selection data to a single query.
|
||||
*
|
||||
* @param selectionData {Array} The selection data generated from getSelectionData
|
||||
* @returns {String} The formatted and uri-encoded query-string.
|
||||
*/
|
||||
selectionDataToQuery: function (selectionData) {
|
||||
var queries = [];
|
||||
|
||||
// create new url
|
||||
if (selectionData.length < 2) {
|
||||
this.icinga.logger.error('Something went wrong, we should never multiselect just one row');
|
||||
} else {
|
||||
$.each(selectionData, function(i, el){
|
||||
var parts = []
|
||||
$.each(el, function(key, value) {
|
||||
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
|
||||
});
|
||||
queries.push('(' + parts.join('&') + ')');
|
||||
});
|
||||
}
|
||||
return '(' + queries.join('|') + ')';
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a single query-argument (not compatible to selectionDataToQuery)
|
||||
*
|
||||
* @param data
|
||||
* @returns {string}
|
||||
*/
|
||||
selectionDataToQueryComp: function(data) {
|
||||
var queries = [];
|
||||
$.each(data, function(key, value){
|
||||
queries.push(key + '=' + encodeURIComponent(value));
|
||||
});
|
||||
return queries.join('&');
|
||||
},
|
||||
|
||||
/**
|
||||
* Store a set of selection-data to preserve it accross page-reloads
|
||||
*
|
||||
* @param data {Array|String|Null} The selection-data be an Array of Objects,
|
||||
* containing the selection data (when multiple rows where selected), a
|
||||
* String containing a single url (when only a single row was selected) or
|
||||
* Null when nothing was selected.
|
||||
*/
|
||||
storeSelectionData: function(data) {
|
||||
selectionData = data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the last stored set of selection-data
|
||||
*
|
||||
* @returns {Array|String|Null} May be an Array of Objects, containing the selection data
|
||||
* (when multiple rows where selected), a String containing a single url
|
||||
* (when only a single row was selected) or Null when nothing was selected.
|
||||
*/
|
||||
loadSelectionData: function() {
|
||||
this.provideSelectionCount();
|
||||
return selectionData;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the selections row count hint info
|
||||
*/
|
||||
provideSelectionCount: function() {
|
||||
var $count = $('.selection-info-count');
|
||||
|
||||
if (typeof selectionData === 'undefined' || selectionData === null) {
|
||||
$count.text(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof selectionData === 'string') {
|
||||
$count.text(1);
|
||||
} else if (selectionData.length > 1) {
|
||||
$count.text(selectionData.length);
|
||||
} else {
|
||||
$count.text(0);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the given table by deselecting all selections on all other tables.
|
||||
*
|
||||
* Focusing a table is important for environments with multiple tables like
|
||||
* the dashboard. It should only be possible to select rows at one table at a time,
|
||||
* when a user selects a row on a table all rows that are not child of the given table
|
||||
* will be removed from the selection.
|
||||
*
|
||||
* @param table {htmlElement} The table to focus.
|
||||
*/
|
||||
focusTable: function (table) {
|
||||
$('table').filter(function(){ return this !== table; }).find('tr[href]').removeClass('active');
|
||||
var n = $(table).closest('div.container').attr('data-icinga-url');
|
||||
focusedTableDataUrl = n;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the URL of the last focused table container.
|
||||
*
|
||||
* @returns {String} The data-icinga-url of the last focused table, which should be unique in each site.
|
||||
*/
|
||||
getFocusedContainerDataUrl: function() {
|
||||
return focusedTableDataUrl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Assign a unique ID to each .container without such
|
||||
*
|
||||
|
|
|
@ -293,6 +293,14 @@
|
|||
return $element[0];
|
||||
},
|
||||
|
||||
objectKeys: Object.keys || function (obj) {
|
||||
var keys = [];
|
||||
$.each(obj, function (key) {
|
||||
keys.push(key);
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanup
|
||||
*/
|
||||
|
|
|
@ -26,7 +26,7 @@ class DbResourceFormTest extends BaseTestCase
|
|||
public function testValidDbResourceIsValid()
|
||||
{
|
||||
$this->setUpResourceFactoryMock(
|
||||
Mockery::mock()->shouldReceive('getConnection')->atMost()->twice()->andReturn(Mockery::self())->getMock()
|
||||
Mockery::mock()->shouldReceive('inspect')->andReturn(self::createInspector(false))->getMock()
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
|
@ -42,7 +42,7 @@ class DbResourceFormTest extends BaseTestCase
|
|||
public function testInvalidDbResourceIsNotValid()
|
||||
{
|
||||
$this->setUpResourceFactoryMock(
|
||||
Mockery::mock()->shouldReceive('getConnection')->once()->andThrow('\Exception')->getMock()
|
||||
Mockery::mock()->shouldReceive('inspect')->andReturn(self::createInspector(true))->getMock()
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
|
@ -58,4 +58,21 @@ class DbResourceFormTest extends BaseTestCase
|
|||
->with(Mockery::type('Icinga\Data\ConfigObject'))
|
||||
->andReturn($resourceMock);
|
||||
}
|
||||
|
||||
public static function createInspector($error = false, $log = array('log'))
|
||||
{
|
||||
if (! $error) {
|
||||
$calls = array(
|
||||
'hasError' => false,
|
||||
'toArray' => $log
|
||||
);
|
||||
} else {
|
||||
$calls = array(
|
||||
'hasError' => true,
|
||||
'getError' => 'Error',
|
||||
'toArray' => $log
|
||||
);
|
||||
}
|
||||
return Mockery::mock('Icinga\Data\Inspection', $calls);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,14 +26,16 @@ class LdapResourceFormTest extends BaseTestCase
|
|||
public function testValidLdapResourceIsValid()
|
||||
{
|
||||
$this->setUpResourceFactoryMock(
|
||||
Mockery::mock()->shouldReceive('connect')->once()->shouldReceive('bind')->once()->getMock()
|
||||
Mockery::mock()->shouldReceive('inspect')->andReturn(self::createInspector(false))->getMock()
|
||||
);
|
||||
|
||||
// Passing array(null) is required to make Mockery call the constructor...
|
||||
$form = Mockery::mock('Icinga\Forms\Config\Resource\LdapResourceForm[getView]', array(null));
|
||||
$form->shouldReceive('getView->escape')
|
||||
->with(Mockery::type('string'))
|
||||
->andReturnUsing(function ($s) { return $s; });
|
||||
->andReturnUsing(function ($s) {
|
||||
return $s;
|
||||
});
|
||||
$form->setTokenDisabled();
|
||||
|
||||
$this->assertTrue(
|
||||
|
@ -49,14 +51,16 @@ class LdapResourceFormTest extends BaseTestCase
|
|||
public function testInvalidLdapResourceIsNotValid()
|
||||
{
|
||||
$this->setUpResourceFactoryMock(
|
||||
Mockery::mock()->shouldReceive('connect')->once()->shouldReceive('bind')->andThrow('\Exception')->getMock()
|
||||
Mockery::mock()->shouldReceive('inspect')->andReturn(self::createInspector(true))->getMock()
|
||||
);
|
||||
|
||||
// Passing array(null) is required to make Mockery call the constructor...
|
||||
$form = Mockery::mock('Icinga\Forms\Config\Resource\LdapResourceForm[getView]', array(null));
|
||||
$form->shouldReceive('getView->escape')
|
||||
->with(Mockery::type('string'))
|
||||
->andReturnUsing(function ($s) { return $s; });
|
||||
->andReturnUsing(function ($s) {
|
||||
return $s;
|
||||
});
|
||||
$form->setTokenDisabled();
|
||||
|
||||
$this->assertFalse(
|
||||
|
@ -72,4 +76,21 @@ class LdapResourceFormTest extends BaseTestCase
|
|||
->with(Mockery::type('Icinga\Data\ConfigObject'))
|
||||
->andReturn($resourceMock);
|
||||
}
|
||||
|
||||
public static function createInspector($error = false, $log = array('log'))
|
||||
{
|
||||
if (! $error) {
|
||||
$calls = array(
|
||||
'hasError' => false,
|
||||
'toArray' => $log
|
||||
);
|
||||
} else {
|
||||
$calls = array(
|
||||
'hasError' => true,
|
||||
'getError' => 'Error',
|
||||
'toArray' => $log
|
||||
);
|
||||
}
|
||||
return Mockery::mock('Icinga\Data\Inspection', $calls);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,14 +28,16 @@ class DbBackendFormTest extends BaseTestCase
|
|||
{
|
||||
$this->setUpResourceFactoryMock();
|
||||
Mockery::mock('overload:Icinga\Authentication\User\DbUserBackend')
|
||||
->shouldReceive('select->where->count')
|
||||
->andReturn(2);
|
||||
->shouldReceive('inspect')
|
||||
->andReturn(self::createInspector(false));
|
||||
|
||||
// Passing array(null) is required to make Mockery call the constructor...
|
||||
$form = Mockery::mock('Icinga\Forms\Config\UserBackend\DbBackendForm[getView]', array(null));
|
||||
$form->shouldReceive('getView->escape')
|
||||
->with(Mockery::type('string'))
|
||||
->andReturnUsing(function ($s) { return $s; });
|
||||
->andReturnUsing(function ($s) {
|
||||
return $s;
|
||||
});
|
||||
$form->setTokenDisabled();
|
||||
$form->setResources(array('test_db_backend'));
|
||||
$form->populate(array('resource' => 'test_db_backend'));
|
||||
|
@ -54,14 +56,16 @@ class DbBackendFormTest extends BaseTestCase
|
|||
{
|
||||
$this->setUpResourceFactoryMock();
|
||||
Mockery::mock('overload:Icinga\Authentication\User\DbUserBackend')
|
||||
->shouldReceive('count')
|
||||
->andReturn(0);
|
||||
->shouldReceive('inspect')
|
||||
->andReturn(self::createInspector(true));
|
||||
|
||||
// Passing array(null) is required to make Mockery call the constructor...
|
||||
$form = Mockery::mock('Icinga\Forms\Config\UserBackend\DbBackendForm[getView]', array(null));
|
||||
$form->shouldReceive('getView->escape')
|
||||
->with(Mockery::type('string'))
|
||||
->andReturnUsing(function ($s) { return $s; });
|
||||
->andReturnUsing(function ($s) {
|
||||
return $s;
|
||||
});
|
||||
$form->setTokenDisabled();
|
||||
$form->setResources(array('test_db_backend'));
|
||||
$form->populate(array('resource' => 'test_db_backend'));
|
||||
|
@ -80,4 +84,21 @@ class DbBackendFormTest extends BaseTestCase
|
|||
->shouldReceive('getResourceConfig')
|
||||
->andReturn(new ConfigObject());
|
||||
}
|
||||
|
||||
public static function createInspector($error = false, $log = array('log'))
|
||||
{
|
||||
if (! $error) {
|
||||
$calls = array(
|
||||
'hasError' => false,
|
||||
'toArray' => $log
|
||||
);
|
||||
} else {
|
||||
$calls = array(
|
||||
'hasError' => true,
|
||||
'getError' => 'Error',
|
||||
'toArray' => $log
|
||||
);
|
||||
}
|
||||
return Mockery::mock('Icinga\Data\Inspection', $calls);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use Icinga\Data\ConfigObject;
|
|||
use Icinga\Test\BaseTestCase;
|
||||
use Icinga\Forms\Config\UserBackend\LdapBackendForm;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Tests\Icinga\Forms\Config\Resource\LdapResourceFormTest;
|
||||
|
||||
class LdapBackendFormTest extends BaseTestCase
|
||||
{
|
||||
|
@ -27,15 +28,18 @@ class LdapBackendFormTest extends BaseTestCase
|
|||
*/
|
||||
public function testValidBackendIsValid()
|
||||
{
|
||||
$ldapUserBackendMock = Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend');
|
||||
$ldapUserBackendMock->shouldReceive('assertAuthenticationPossible')->andReturnNull();
|
||||
$ldapUserBackendMock = Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend')
|
||||
->shouldReceive('inspect')
|
||||
->andReturn(self::createInspector(false))->getMock();
|
||||
$this->setUpUserBackendMock($ldapUserBackendMock);
|
||||
|
||||
// Passing array(null) is required to make Mockery call the constructor...
|
||||
$form = Mockery::mock('Icinga\Forms\Config\UserBackend\LdapBackendForm[getView]', array(null));
|
||||
$form->shouldReceive('getView->escape')
|
||||
->with(Mockery::type('string'))
|
||||
->andReturnUsing(function ($s) { return $s; });
|
||||
->andReturnUsing(function ($s) {
|
||||
return $s;
|
||||
});
|
||||
$form->setTokenDisabled();
|
||||
$form->setResources(array('test_ldap_backend'));
|
||||
$form->populate(array('resource' => 'test_ldap_backend'));
|
||||
|
@ -52,7 +56,9 @@ class LdapBackendFormTest extends BaseTestCase
|
|||
*/
|
||||
public function testInvalidBackendIsNotValid()
|
||||
{
|
||||
$ldapUserBackendMock = Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend');
|
||||
$ldapUserBackendMock = Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend')
|
||||
->shouldReceive('inspect')
|
||||
->andReturn(self::createInspector(true))->getMock();
|
||||
$ldapUserBackendMock->shouldReceive('assertAuthenticationPossible')->andThrow(new AuthenticationException);
|
||||
$this->setUpUserBackendMock($ldapUserBackendMock);
|
||||
|
||||
|
@ -60,7 +66,9 @@ class LdapBackendFormTest extends BaseTestCase
|
|||
$form = Mockery::mock('Icinga\Forms\Config\UserBackend\LdapBackendForm[getView]', array(null));
|
||||
$form->shouldReceive('getView->escape')
|
||||
->with(Mockery::type('string'))
|
||||
->andReturnUsing(function ($s) { return $s; });
|
||||
->andReturnUsing(function ($s) {
|
||||
return $s;
|
||||
});
|
||||
$form->setTokenDisabled();
|
||||
$form->setResources(array('test_ldap_backend'));
|
||||
$form->populate(array('resource' => 'test_ldap_backend'));
|
||||
|
@ -77,4 +85,21 @@ class LdapBackendFormTest extends BaseTestCase
|
|||
->shouldReceive('create')
|
||||
->andReturn($ldapUserBackendMock);
|
||||
}
|
||||
|
||||
public static function createInspector($error = false, $log = array('log'))
|
||||
{
|
||||
if (! $error) {
|
||||
$calls = array(
|
||||
'hasError' => false,
|
||||
'toArray' => $log
|
||||
);
|
||||
} else {
|
||||
$calls = array(
|
||||
'hasError' => true,
|
||||
'getError' => 'Error',
|
||||
'toArray' => $log
|
||||
);
|
||||
}
|
||||
return Mockery::mock('Icinga\Data\Inspection', $calls);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue