From b842a396503f0df8dd7771d07b4bf31069e9a537 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 26 May 2015 18:23:26 +0200 Subject: [PATCH 01/54] Implement ::getLinuxDistro() refs #8705 --- library/Icinga/Application/Platform.php | 118 ++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/library/Icinga/Application/Platform.php b/library/Icinga/Application/Platform.php index 355311b97..a6e0d9604 100644 --- a/library/Icinga/Application/Platform.php +++ b/library/Icinga/Application/Platform.php @@ -3,6 +3,8 @@ namespace Icinga\Application; +use Icinga\Exception\IcingaException; + /** * Platform tests for icingaweb */ @@ -59,6 +61,122 @@ class Platform return strtoupper(substr(self::getOperatingSystemName(), 0, 5)) === 'LINUX'; } + /** + * Return the Linux distribution's name + * or 'linux' if the name could not be found out + * or false if the OS isn't Linux or an error occurred + * + * @param int $reliable + * 3: Only parse /etc/os-release (or /usr/lib/os-release). + * For the paranoid ones. + * 2: If that (3) doesn't help, check /etc/*-release, too. + * If something is unclear, return 'linux'. + * 1: Almost equal to mode 2. The possible return values also include: + * 'redhat' -- unclear whether RHEL/Fedora/... + * 'suse' -- unclear whether SLES/openSUSE/... + * 0: If even that (1) doesn't help, check /proc/version, too. + * This may not work (as expected) on LXC containers! + * (No reliability at all!) + * + * @return string|bool + */ + public static function getLinuxDistro($reliable = 2) + { + if (! self::isLinux()) { + return false; + } + + foreach (array('/etc/os-release', '/usr/lib/os-release') as $osReleaseFile) { + if (false === ($osRelease = @file( + $osReleaseFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + ))) { + continue; + } + + foreach ($osRelease as $osInfo) { + if (false === ($res = @preg_match('/(? 2) { + return 'linux'; + } + + foreach (array( + 'fedora' => '/etc/fedora-release', + 'centos' => '/etc/centos-release' + ) as $distro => $releaseFile) { + if (! (false === ( + $release = @file_get_contents($releaseFile) + ) || false === strpos(strtolower($release), $distro))) { + return $distro; + } + } + + if (false !== ($release = @file_get_contents('/etc/redhat-release'))) { + $release = strtolower($release); + if (false !== strpos($release, 'red hat enterprise linux')) { + return 'rhel'; + } + foreach (array('fedora', 'centos') as $distro) { + if (false !== strpos($release, $distro)) { + return $distro; + } + } + return $reliable < 2 ? 'redhat' : 'linux'; + } + + if (false !== ($release = @file_get_contents('/etc/SuSE-release'))) { + $release = strtolower($release); + foreach (array( + 'opensuse' => 'opensuse', + 'sles' => 'suse linux enterprise server', + 'sled' => 'suse linux enterprise desktop' + ) as $distro => $name) { + if (false !== strpos($release, $name)) { + return $distro; + } + } + return $reliable < 2 ? 'suse' : 'linux'; + } + + if ($reliable < 1) { + if (false === ($procVersion = @file_get_contents('/proc/version'))) { + throw new IcingaException('Failed at file_get_contents(/proc/version)'); + } + $procVersion = strtolower($procVersion); + foreach (array( + 'redhat' => 'red hat', + 'suse' => 'suse linux', + 'ubuntu' => 'ubuntu', + 'debian' => 'debian' + ) as $distro => $name) { + if (false !== strpos($procVersion, $name)) { + return $distro; + } + } + } + + return 'linux'; + } + /** * Test of CLI environment * From 38cecdb724af28ce0e7d44a7b6829a187be5a0ad Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 27 May 2015 11:39:13 +0200 Subject: [PATCH 02/54] Setup Wizard: show how to add a system group "icingaweb2" and how to add the webserver's user to it refs #8705 --- .../views/scripts/form/setup-welcome.phtml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/modules/setup/application/views/scripts/form/setup-welcome.phtml b/modules/setup/application/views/scripts/form/setup-welcome.phtml index 1b37f60f0..9a4c631fa 100644 --- a/modules/setup/application/views/scripts/form/setup-welcome.phtml +++ b/modules/setup/application/views/scripts/form/setup-welcome.phtml @@ -10,6 +10,39 @@ $configDir = Icinga::app()->getConfigDir(); $setupTokenPath = rtrim($configDir, '/') . '/setup.token'; $cliPath = realpath(Icinga::app()->getApplicationDir() . '/../bin/icingacli'); +$groupadd = null; +$usermod = null; +if (! (false === ($distro = Platform::getLinuxDistro(1)) || $distro === 'linux')) { + foreach (array( + 'groupadd -r icingaweb2' => array( + 'redhat', 'rhel', 'centos', 'fedora', + 'suse', 'sles', 'sled', 'opensuse' + ), + 'addgroup --system icingaweb2' => array('debian', 'ubuntu') + ) as $groupadd_ => $distros) { + if (in_array($distro, $distros)) { + $groupadd = $groupadd_; + break; + } + } + + foreach (array( + 'usermod -a -G icingaweb2 apache' => array( + 'redhat', 'rhel', 'centos', 'fedora' + ), + 'usermod -A icingaweb2 wwwrun' => array( + 'suse', 'sles', 'sled', 'opensuse' + ), + 'usermod -a -G icingaweb2 www-data' => array( + 'debian', 'ubuntu' + ) + ) as $usermod_ => $distros) { + if (in_array($distro, $distros)) { + $usermod = $usermod_; + break; + } + } +} ?>

translate('Welcome to the configuration of Icinga Web 2!') ?>

@@ -51,6 +84,12 @@ $cliPath = realpath(Icinga::app()->getApplicationDir() . '/../bin/icingacli');
  • translate('Your webserver\'s user is a member of the system group "icingaweb2"'); ?>
  • + +
    + escape($groupadd . ';') ?> + escape($usermod . ';') ?> +
    +

    translate('If you\'ve got the IcingaCLI installed you can do the following:'); ?>

    setup config directory --group icingaweb2; From 441fc336437c2ed9ae2e44f6ebc5e9928e20897b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2015 11:53:43 +0200 Subject: [PATCH 03/54] Include version information on `git archive' refs #9247 --- .gitattributes | 3 +++ application/VERSION | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 application/VERSION diff --git a/.gitattributes b/.gitattributes index a05c2f5cd..6ae8ee7af 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,6 @@ Vagrantfile export-ignore # Normalize puppet manifests' line endings to LF on checkin and prevent conversion to CRLF when the files are checked out .puppet* eol=lf + +# Include version information on `git archive' +/application/VERSION export-subst diff --git a/application/VERSION b/application/VERSION new file mode 100644 index 000000000..47f3a662f --- /dev/null +++ b/application/VERSION @@ -0,0 +1,2 @@ +GitCommitID: $Format:%H%d$ +GitCommitDate: $Format:%ci$ From 887bd0b8f04b76a57b27b7a46cb8333c5e5d1755 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2015 16:43:32 +0200 Subject: [PATCH 04/54] Show Icinga Web 2's version in the frontend refs #9247 --- application/controllers/AboutController.php | 91 +++++++++++++++++++++ application/views/scripts/about/index.phtml | 20 +++++ 2 files changed, 111 insertions(+) create mode 100644 application/controllers/AboutController.php create mode 100644 application/views/scripts/about/index.phtml diff --git a/application/controllers/AboutController.php b/application/controllers/AboutController.php new file mode 100644 index 000000000..6572d7e8b --- /dev/null +++ b/application/controllers/AboutController.php @@ -0,0 +1,91 @@ +view->appVersion = null; + $this->view->gitCommitID = null; + $this->view->gitCommitDate = null; + + if (false !== ($appVersion = @file( + Icinga::app()->getApplicationDir() . DIRECTORY_SEPARATOR . 'VERSION', + FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + ))) { + foreach ($appVersion as $av) { + $matches = array(); + if (false === ($res = preg_match( + '/(?view->gitCommitID !== null) { + break; + } + + $matches2 = array(); + if (false === ($res = preg_match( + '/(?view->gitCommitID = $matches2[1]; + if (! isset($matches2[2])) { + break; + } + + foreach (preg_split( + '/\s*,\s*/', $matches2[2], -1, PREG_SPLIT_NO_EMPTY + ) as $refName) { + $matches3 = array(); + if (false === ($res = preg_match( + '/(?view->appVersion = $matches3[1]; + break; + } + } + break; + case 'GitCommitDate': + if ($this->view->gitCommitDate !== null) { + break; + } + + $matches2 = array(); + if (false === ($res = preg_match( + '/(?view->gitCommitDate = $matches2[1]; + } + } + } + } + } +} diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml new file mode 100644 index 000000000..0429a5374 --- /dev/null +++ b/application/views/scripts/about/index.phtml @@ -0,0 +1,20 @@ +
    +

    Icinga Web 2

    + translate('Version: %s'), $appVersion), + array($this->translate('Git commit ID: %s'), $gitCommitID), + array($this->translate('Git commit date: %s'), $gitCommitDate) + ) as $version) { + list($label, $value) = $version; + if ($value !== null) { + $versionInfo[] = sprintf($label, htmlspecialchars($value)); + } + } + ?> +

    translate('unknown version') + : nl2br(implode("\n", $versionInfo), false) + ?>

    +
    From 5b8de49cdf46ad6499f33a90252e5822cb83fc82 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2015 17:16:27 +0200 Subject: [PATCH 05/54] Link to the about page refs #9247 --- library/Icinga/Web/Menu.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/Icinga/Web/Menu.php b/library/Icinga/Web/Menu.php index 63d92fc9f..45dd2dd9c 100644 --- a/library/Icinga/Web/Menu.php +++ b/library/Icinga/Web/Menu.php @@ -279,6 +279,11 @@ class Menu implements RecursiveIterator 'priority' => 990, 'renderer' => 'ForeignMenuItemRenderer' )); + + $this->add(t('About'), array( + 'url' => 'about', + 'priority' => 1000 + )); } } From e0c0e9c8743ce4b9a1d364c75a96be5fbdfc2da3 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 3 Jun 2015 14:36:46 +0200 Subject: [PATCH 06/54] LdapUserBackend: Move function retrieveGeneralizedTime into its parent refs #7343 --- .../Authentication/User/LdapUserBackend.php | 32 ------------------- library/Icinga/Repository/Repository.php | 31 ++++++++++++++++++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/library/Icinga/Authentication/User/LdapUserBackend.php b/library/Icinga/Authentication/User/LdapUserBackend.php index 154a33a44..64ce0ac24 100644 --- a/library/Icinga/Authentication/User/LdapUserBackend.php +++ b/library/Icinga/Authentication/User/LdapUserBackend.php @@ -4,7 +4,6 @@ namespace Icinga\Authentication\User; use DateTime; -use Icinga\Application\Logger; use Icinga\Data\ConfigObject; use Icinga\Exception\AuthenticationException; use Icinga\Exception\ProgrammingError; @@ -325,37 +324,6 @@ class LdapUserBackend extends Repository implements UserBackendInterface return ((int) $value & $ADS_UF_ACCOUNTDISABLE) === 0; } - /** - * Parse the given value based on the ASN.1 standard (GeneralizedTime) and return its timestamp representation - * - * @param string|null $value - * - * @return int - */ - protected function retrieveGeneralizedTime($value) - { - if ($value === null) { - return $value; - } - - if ( - ($dateTime = DateTime::createFromFormat('YmdHis.uO', $value)) !== false - || ($dateTime = DateTime::createFromFormat('YmdHis.uZ', $value)) !== false - || ($dateTime = DateTime::createFromFormat('YmdHis.u', $value)) !== false - || ($dateTime = DateTime::createFromFormat('YmdHis', $value)) !== false - || ($dateTime = DateTime::createFromFormat('YmdHi', $value)) !== false - || ($dateTime = DateTime::createFromFormat('YmdH', $value)) !== false - ) { - return $dateTime->getTimeStamp(); - } else { - Logger::debug(sprintf( - 'Failed to parse "%s" based on the ASN.1 standard (GeneralizedTime) for user backend "%s".', - $value, - $this->getName() - )); - } - } - /** * Return whether the given shadowExpire value defines that a user is permitted to login * diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 803f4958f..98b3e0ab4 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -599,6 +599,37 @@ abstract class Repository implements Selectable return $value; } + /** + * Parse the given value based on the ASN.1 standard (GeneralizedTime) and return its timestamp representation + * + * @param string|null $value + * + * @return int + */ + protected function retrieveGeneralizedTime($value) + { + if ($value === null) { + return $value; + } + + if ( + ($dateTime = DateTime::createFromFormat('YmdHis.uO', $value)) !== false + || ($dateTime = DateTime::createFromFormat('YmdHis.uZ', $value)) !== false + || ($dateTime = DateTime::createFromFormat('YmdHis.u', $value)) !== false + || ($dateTime = DateTime::createFromFormat('YmdHis', $value)) !== false + || ($dateTime = DateTime::createFromFormat('YmdHi', $value)) !== false + || ($dateTime = DateTime::createFromFormat('YmdH', $value)) !== false + ) { + return $dateTime->getTimeStamp(); + } else { + Logger::debug(sprintf( + 'Failed to parse "%s" based on the ASN.1 standard (GeneralizedTime) in repository "%s".', + $value, + $this->getName() + )); + } + } + /** * Validate that the requested table exists * From 96f5f8fd4950e40bbe5121c435cf7bda9b1838a6 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 3 Jun 2015 15:16:54 +0200 Subject: [PATCH 07/54] LdapUserBackend: Do not fetch a user's groups refs #7343 --- .../Authentication/User/LdapUserBackend.php | 58 +------------------ .../Authentication/User/UserBackend.php | 12 ---- 2 files changed, 1 insertion(+), 69 deletions(-) diff --git a/library/Icinga/Authentication/User/LdapUserBackend.php b/library/Icinga/Authentication/User/LdapUserBackend.php index 64ce0ac24..e85a39029 100644 --- a/library/Icinga/Authentication/User/LdapUserBackend.php +++ b/library/Icinga/Authentication/User/LdapUserBackend.php @@ -64,8 +64,6 @@ class LdapUserBackend extends Repository implements UserBackendInterface ) ); - protected $groupOptions; - /** * Normed attribute names based on known LDAP environments * @@ -178,17 +176,6 @@ class LdapUserBackend extends Repository implements UserBackendInterface return $this->filter; } - public function setGroupOptions(array $options) - { - $this->groupOptions = $options; - return $this; - } - - public function getGroupOptions() - { - return $this->groupOptions; - } - /** * Return the given attribute name normed to known LDAP enviroments, if possible * @@ -381,41 +368,6 @@ class LdapUserBackend extends Repository implements UserBackendInterface } } - /** - * Retrieve the user groups - * - * @TODO: Subject to change, see #7343 - * - * @param string $dn - * - * @return array - */ - public function getGroups($dn) - { - if (empty($this->groupOptions) || ! isset($this->groupOptions['group_base_dn'])) { - return array(); - } - - $result = $this->ds->select() - ->setBase($this->groupOptions['group_base_dn']) - ->from( - $this->groupOptions['group_class'], - array($this->groupOptions['group_attribute']) - ) - ->where( - $this->groupOptions['group_member_attribute'], - $dn - ) - ->fetchAll(); - - $groups = array(); - foreach ($result as $group) { - $groups[] = $group->{$this->groupOptions['group_attribute']}; - } - - return $groups; - } - /** * Authenticate the given user * @@ -440,15 +392,7 @@ class LdapUserBackend extends Repository implements UserBackendInterface return false; } - $authenticated = $this->ds->testCredentials($userDn, $password); - if ($authenticated) { - $groups = $this->getGroups($userDn); - if ($groups !== null) { - $user->setGroups($groups); - } - } - - return $authenticated; + return $this->ds->testCredentials($userDn, $password); } catch (LdapException $e) { throw new AuthenticationException( 'Failed to authenticate user "%s" against backend "%s". An exception was thrown:', diff --git a/library/Icinga/Authentication/User/UserBackend.php b/library/Icinga/Authentication/User/UserBackend.php index 3d11289fb..42a4c2b42 100644 --- a/library/Icinga/Authentication/User/UserBackend.php +++ b/library/Icinga/Authentication/User/UserBackend.php @@ -165,12 +165,6 @@ class UserBackend $backend->setUserClass($backendConfig->get('user_class', 'user')); $backend->setUserNameAttribute($backendConfig->get('user_name_attribute', 'sAMAccountName')); $backend->setFilter($backendConfig->filter); - $backend->setGroupOptions(array( - 'group_base_dn' => $backendConfig->get('group_base_dn', $resource->getDN()), - 'group_attribute' => $backendConfig->get('group_attribute', 'sAMAccountName'), - 'group_member_attribute' => $backendConfig->get('group_member_attribute', 'member'), - 'group_class' => $backendConfig->get('group_class', 'group') - )); break; case 'ldap': $backend = new LdapUserBackend($resource); @@ -178,12 +172,6 @@ class UserBackend $backend->setUserClass($backendConfig->get('user_class', 'inetOrgPerson')); $backend->setUserNameAttribute($backendConfig->get('user_name_attribute', 'uid')); $backend->setFilter($backendConfig->filter); - $backend->setGroupOptions(array( - 'group_base_dn' => $backendConfig->group_base_dn, - 'group_attribute' => $backendConfig->group_attribute, - 'group_member_attribute' => $backendConfig->group_member_attribute, - 'group_class' => $backendConfig->group_class - )); break; } From 757e993871f4409678bd3064d1bd40eb18cec3e6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 3 Jun 2015 14:40:58 +0200 Subject: [PATCH 08/54] Outsource version-getting logic to Version::get() refs #9247 --- application/controllers/AboutController.php | 80 +-------------- application/views/scripts/about/index.phtml | 17 ++-- library/Icinga/Version.php | 105 ++++++++++++++++++++ 3 files changed, 116 insertions(+), 86 deletions(-) create mode 100644 library/Icinga/Version.php diff --git a/application/controllers/AboutController.php b/application/controllers/AboutController.php index 6572d7e8b..376490443 100644 --- a/application/controllers/AboutController.php +++ b/application/controllers/AboutController.php @@ -4,88 +4,12 @@ # namespace Icinga\Application\Controllers; use Icinga\Web\Controller\ActionController; -use Icinga\Application\Icinga; -use Icinga\Exception\IcingaException; +use Icinga\Version; class AboutController extends ActionController { public function indexAction() { - $this->view->appVersion = null; - $this->view->gitCommitID = null; - $this->view->gitCommitDate = null; - - if (false !== ($appVersion = @file( - Icinga::app()->getApplicationDir() . DIRECTORY_SEPARATOR . 'VERSION', - FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES - ))) { - foreach ($appVersion as $av) { - $matches = array(); - if (false === ($res = preg_match( - '/(?view->gitCommitID !== null) { - break; - } - - $matches2 = array(); - if (false === ($res = preg_match( - '/(?view->gitCommitID = $matches2[1]; - if (! isset($matches2[2])) { - break; - } - - foreach (preg_split( - '/\s*,\s*/', $matches2[2], -1, PREG_SPLIT_NO_EMPTY - ) as $refName) { - $matches3 = array(); - if (false === ($res = preg_match( - '/(?view->appVersion = $matches3[1]; - break; - } - } - break; - case 'GitCommitDate': - if ($this->view->gitCommitDate !== null) { - break; - } - - $matches2 = array(); - if (false === ($res = preg_match( - '/(?view->gitCommitDate = $matches2[1]; - } - } - } - } + $this->view->version = Version::get(); } } diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml index 0429a5374..3c37a5e73 100644 --- a/application/views/scripts/about/index.phtml +++ b/application/views/scripts/about/index.phtml @@ -2,14 +2,15 @@

    Icinga Web 2

    translate('Version: %s'), $appVersion), - array($this->translate('Git commit ID: %s'), $gitCommitID), - array($this->translate('Git commit date: %s'), $gitCommitDate) - ) as $version) { - list($label, $value) = $version; - if ($value !== null) { - $versionInfo[] = sprintf($label, htmlspecialchars($value)); + if ($version !== false) { + foreach (array( + 'appVersion' => $this->translate('Version: %s'), + 'gitCommitID' => $this->translate('Git commit ID: %s'), + 'gitCommitDate' => $this->translate('Git commit date: %s') + ) as $key => $label) { + if (null !== ($value = $version[$key])) { + $versionInfo[] = sprintf($label, htmlspecialchars($value)); + } } } ?> diff --git a/library/Icinga/Version.php b/library/Icinga/Version.php new file mode 100644 index 000000000..130e75060 --- /dev/null +++ b/library/Icinga/Version.php @@ -0,0 +1,105 @@ + null, + 'gitCommitID' => null, + 'gitCommitDate' => null + ); + + if (false !== ($appVersion = @file( + Icinga::app()->getApplicationDir() . DIRECTORY_SEPARATOR . 'VERSION', + FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + ))) { + foreach ($appVersion as $av) { + $matches = array(); + if (false === ($res = preg_match( + '/(? Date: Wed, 3 Jun 2015 15:28:07 +0200 Subject: [PATCH 09/54] Introduce class LdapRepository refs #7343 --- .../Authentication/User/LdapUserBackend.php | 33 +--------- library/Icinga/Repository/LdapRepository.php | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+), 31 deletions(-) create mode 100644 library/Icinga/Repository/LdapRepository.php diff --git a/library/Icinga/Authentication/User/LdapUserBackend.php b/library/Icinga/Authentication/User/LdapUserBackend.php index e85a39029..333969891 100644 --- a/library/Icinga/Authentication/User/LdapUserBackend.php +++ b/library/Icinga/Authentication/User/LdapUserBackend.php @@ -7,13 +7,13 @@ use DateTime; use Icinga\Data\ConfigObject; use Icinga\Exception\AuthenticationException; use Icinga\Exception\ProgrammingError; -use Icinga\Repository\Repository; +use Icinga\Repository\LdapRepository; use Icinga\Repository\RepositoryQuery; use Icinga\Protocol\Ldap\Exception as LdapException; use Icinga\Protocol\Ldap\Expression; use Icinga\User; -class LdapUserBackend extends Repository implements UserBackendInterface +class LdapUserBackend extends LdapRepository implements UserBackendInterface { /** * The base DN to use for a query @@ -64,18 +64,6 @@ class LdapUserBackend extends Repository implements UserBackendInterface ) ); - /** - * Normed attribute names based on known LDAP environments - * - * @var array - */ - protected $normedAttributes = array( - 'uid' => 'uid', - 'user' => 'user', - 'inetorgperson' => 'inetOrgPerson', - 'samaccountname' => 'sAMAccountName' - ); - /** * Set the base DN to use for a query * @@ -176,23 +164,6 @@ class LdapUserBackend extends Repository implements UserBackendInterface return $this->filter; } - /** - * Return the given attribute name normed to known LDAP enviroments, if possible - * - * @param string $name - * - * @return string - */ - protected function getNormedAttribute($name) - { - $loweredName = strtolower($name); - if (array_key_exists($loweredName, $this->normedAttributes)) { - return $this->normedAttributes[$loweredName]; - } - - return $name; - } - /** * Apply the given configuration to this backend * diff --git a/library/Icinga/Repository/LdapRepository.php b/library/Icinga/Repository/LdapRepository.php new file mode 100644 index 000000000..15594d269 --- /dev/null +++ b/library/Icinga/Repository/LdapRepository.php @@ -0,0 +1,65 @@ + + *
  • Attribute name normalization
  • + * + */ +abstract class LdapRepository extends Repository +{ + /** + * The datasource being used + * + * @var Connection + */ + protected $ds; + + /** + * Normed attribute names based on known LDAP environments + * + * @var array + */ + protected $normedAttributes = array( + 'uid' => 'uid', + 'user' => 'user', + 'group' => 'group', + 'member' => 'member', + 'inetorgperson' => 'inetOrgPerson', + 'samaccountname' => 'sAMAccountName' + ); + + /** + * Create a new LDAP repository object + * + * @param Connection $ds The data source to use + */ + public function __construct(Connection $ds) + { + parent::__construct($ds); + } + + /** + * Return the given attribute name normed to known LDAP enviroments, if possible + * + * @param string $name + * + * @return string + */ + protected function getNormedAttribute($name) + { + $loweredName = strtolower($name); + if (array_key_exists($loweredName, $this->normedAttributes)) { + return $this->normedAttributes[$loweredName]; + } + + return $name; + } +} \ No newline at end of file From e65cf954e69ec750f0ebe89eff3a4c5f144170b1 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 3 Jun 2015 16:08:29 +0200 Subject: [PATCH 10/54] LdapRepository: Add normed attribute `gid' refs #7343 --- library/Icinga/Repository/LdapRepository.php | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Icinga/Repository/LdapRepository.php b/library/Icinga/Repository/LdapRepository.php index 15594d269..7cf00ae66 100644 --- a/library/Icinga/Repository/LdapRepository.php +++ b/library/Icinga/Repository/LdapRepository.php @@ -29,6 +29,7 @@ abstract class LdapRepository extends Repository */ protected $normedAttributes = array( 'uid' => 'uid', + 'gid' => 'gid', 'user' => 'user', 'group' => 'group', 'member' => 'member', From 89d992278baf8e4fcd56255746f77a8f2850fdd9 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 3 Jun 2015 16:27:50 +0200 Subject: [PATCH 11/54] Introduce class LdapUserGroupBackend refs #743 --- .../UserGroup/LdapUserGroupBackend.php | 376 ++++++++++++++++++ .../UserGroup/UserGroupBackend.php | 24 ++ 2 files changed, 400 insertions(+) create mode 100644 library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php new file mode 100644 index 000000000..1e6addba2 --- /dev/null +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -0,0 +1,376 @@ + array( + 'order' => 'asc' + ) + ); + + /** + * Set the base DN to use for a user query + * + * @param string $baseDn + * + * @return $this + */ + public function setUserBaseDn($baseDn) + { + if (($baseDn = trim($baseDn))) { + $this->userBaseDn = $baseDn; + } + + return $this; + } + + /** + * Return the base DN to use for a user query + * + * @return string + */ + public function getUserBaseDn() + { + return $this->userBaseDn; + } + + /** + * Set the base DN to use for a group query + * + * @param string $baseDn + * + * @return $this + */ + public function setGroupBaseDn($baseDn) + { + if (($baseDn = trim($baseDn))) { + $this->groupBaseDn = $baseDn; + } + + return $this; + } + + /** + * Return the base DN to use for a group query + * + * @return string + */ + public function getGroupBaseDn() + { + return $this->groupBaseDn; + } + + /** + * Set the objectClass where to look for users + * + * @param string $userClass + * + * @return $this + */ + public function setUserClass($userClass) + { + $this->userClass = $this->getNormedAttribute($userClass); + return $this; + } + + /** + * Return the objectClass where to look for users + * + * @return string + */ + public function getUserClass() + { + return $this->userClass; + } + + /** + * Set the objectClass where to look for groups + * + * Sets also the base table name for the underlying repository. + * + * @param string $groupClass + * + * @return $this + */ + public function setGroupClass($groupClass) + { + $this->baseTable = $this->groupClass = $this->getNormedAttribute($groupClass); + return $this; + } + + /** + * Return the objectClass where to look for groups + * + * @return string + */ + public function getGroupClass() + { + return $this->groupClass; + } + + /** + * Set the attribute name where to find a user's name + * + * @param string $userNameAttribute + * + * @return $this + */ + public function setUserNameAttribute($userNameAttribute) + { + $this->userNameAttribute = $this->getNormedAttribute($userNameAttribute); + return $this; + } + + /** + * Return the attribute name where to find a user's name + * + * @return string + */ + public function getUserNameAttribute() + { + return $this->userNameAttribute; + } + + /** + * Set the attribute name where to find a group's name + * + * @param string $groupNameAttribute + * + * @return $this + */ + public function setGroupNameAttribute($groupNameAttribute) + { + $this->groupNameAttribute = $this->getNormedAttribute($groupNameAttribute); + return $this; + } + + /** + * Return the attribute name where to find a group's name + * + * @return string + */ + public function getGroupNameAttribute() + { + return $this->groupNameAttribute; + } + + /** + * Set the attribute name where to find a group's member + * + * @param string $groupMemberAttribute + * + * @return $this + */ + public function setGroupMemberAttribute($groupMemberAttribute) + { + $this->groupMemberAttribute = $this->getNormedAttribute($groupMemberAttribute); + return $this; + } + + /** + * Return the attribute name where to find a group's member + * + * @return string + */ + public function getGroupMemberAttribute() + { + return $this->groupMemberAttribute; + } + + /** + * Return a new query for the given columns + * + * @param array $columns The desired columns, if null all columns will be queried + * + * @return RepositoryQuery + */ + public function select(array $columns = null) + { + $query = parent::select($columns); + $query->getQuery()->setBase($this->groupBaseDn); + return $query; + } + + /** + * Initialize this repository's query columns + * + * @return array + * + * @throws ProgrammingError In case either $this->groupNameAttribute or $this->groupClass has not been set yet + */ + protected function initializeQueryColumns() + { + if ($this->groupClass === null) { + throw new ProgrammingError('It is required to set the objectClass where to look for groups first'); + } + if ($this->groupNameAttribute === null) { + throw new ProgrammingError('It is required to set a attribute name where to find a group\'s name first'); + } + + if ($this->ds->getCapabilities()->hasAdOid()) { + $createdAtAttribute = 'whenCreated'; + $lastModifiedAttribute = 'whenChanged'; + } else { + $createdAtAttribute = 'createTimestamp'; + $lastModifiedAttribute = 'modifyTimestamp'; + } + + $columns = array( + 'group' => $this->groupNameAttribute, + 'group_name' => $this->groupNameAttribute, + 'user' => $this->groupMemberAttribute, + 'user_name' => $this->groupMemberAttribute, + 'created_at' => $createdAtAttribute, + 'last_modified' => $lastModifiedAttribute + ); + return array('group' => $columns, 'group_membership' => $columns); + } + + /** + * Initialize this repository's conversion rules + * + * @return array + * + * @throws ProgrammingError In case $this->groupClass has not been set yet + */ + protected function initializeConversionRules() + { + if ($this->groupClass === null) { + throw new ProgrammingError('It is required to set the objectClass where to look for groups first'); + } + + return array( + $this->groupClass => array( + 'created_at' => 'generalized_time', + 'last_modified' => 'generalized_time' + ) + ); + } + + /** + * Validate that the requested table exists + * + * This will return $this->groupClass in case $table equals "group" or "group_membership". + * + * @param string $table The table to validate + * @param RepositoryQuery $query An optional query to pass as context + * (unused by the base implementation) + * + * @return string + * + * @throws ProgrammingError In case the given table does not exist + */ + public function requireTable($table, RepositoryQuery $query = null) + { + $table = parent::requireTable($table, $query); + if ($table === 'group' || $table === 'group_membership') { + $table = $this->groupClass; + } + + return $table; + } + + /** + * Return the groups the given user is a member of + * + * @param User $user + * + * @return array + */ + public function getMemberships(User $user) + { + $userDn = $this->ds + ->select() + ->from($this->userClass) + ->where($this->userNameAttribute, $user->getUsername()) + ->setBase($this->userBaseDn) + ->setUsePagedResults(false) + ->fetchDn(); + + if ($userDn === null) { + return array(); + } + + $groupQuery = $this->ds + ->select() + ->from($this->groupClass, array($this->groupNameAttribute)) + ->where($this->groupMemberAttribute, $userDn) + ->setBase($this->groupBaseDn); + + $groups = array(); + foreach ($groupQuery as $row) { + $groups[] = $row->{$this->groupNameAttribute}; + } + + return $groups; + } +} diff --git a/library/Icinga/Authentication/UserGroup/UserGroupBackend.php b/library/Icinga/Authentication/UserGroup/UserGroupBackend.php index dd4900ea8..585f7c449 100644 --- a/library/Icinga/Authentication/UserGroup/UserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/UserGroupBackend.php @@ -21,6 +21,8 @@ class UserGroupBackend */ protected static $defaultBackends = array( 'db', + 'ldap', + 'msldap', //'ini' ); @@ -156,6 +158,28 @@ class UserGroupBackend case 'ini': $backend = new IniUserGroupBackend($resource); break; + case 'ldap': + $backend = new LdapUserGroupBackend($resource); + $backend + ->setGroupBaseDn($backendConfig->base_dn) + ->setUserBaseDn($backendConfig->get('user_base_dn', $backend->getGroupBaseDn())) + ->setGroupClass($backendConfig->get('group_class', 'group')) + ->setUserClass($backendConfig->get('user_class', 'inetOrgPerson')) + ->setGroupNameAttribute($backendConfig->get('group_name_attribute', 'gid')) + ->setUserNameAttribute($backendConfig->get('user_name_attribute', 'uid')) + ->setGroupMemberAttribute($backendConfig->get('group_member_attribute', 'member')); + break; + case 'msldap': + $backend = new LdapUserGroupBackend($resource); + $backend + ->setGroupBaseDn($backendConfig->base_dn) + ->setUserBaseDn($backendConfig->get('user_base_dn', $backend->getGroupBaseDn())) + ->setGroupClass($backendConfig->get('group_class', 'group')) + ->setUserClass($backendConfig->get('user_class', 'user')) + ->setGroupNameAttribute($backendConfig->get('group_name_attribute', 'sAMAccountName')) + ->setUserNameAttribute($backendConfig->get('user_name_attribute', $backend->getGroupNameAttribute())) + ->setGroupMemberAttribute($backendConfig->get('group_member_attribute', 'member')); + break; } $backend->setName($name); From d9eb8f9e8dc8bead46b738fd1aa6270ec7ce8e0e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 3 Jun 2015 16:33:22 +0200 Subject: [PATCH 12/54] LdapUserGroupBackend: Do not extend LdapRepository Selecting groups works, but not memberships. Does not make sense until both things work... refs #7343 --- .../UserGroup/LdapUserGroupBackend.php | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index 1e6addba2..1873cdc8e 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -8,7 +8,7 @@ use Icinga\Repository\LdapRepository; use Icinga\Repository\RepositoryQuery; use Icinga\User; -class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInterface +class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBackendInterface { /** * The base DN to use for a user query @@ -77,6 +77,70 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt ) ); + /** + * Normed attribute names based on known LDAP environments + * + * @var array + */ + protected $normedAttributes = array( + 'uid' => 'uid', + 'gid' => 'gid', + 'user' => 'user', + 'group' => 'group', + 'member' => 'member', + 'inetorgperson' => 'inetOrgPerson', + 'samaccountname' => 'sAMAccountName' + ); + + /** + * The name of this repository + * + * @var string + */ + protected $name; + + /** + * Return the given attribute name normed to known LDAP enviroments, if possible + * + * @param string $name + * + * @return string + */ + protected function getNormedAttribute($name) + { + $loweredName = strtolower($name); + if (array_key_exists($loweredName, $this->normedAttributes)) { + return $this->normedAttributes[$loweredName]; + } + + return $name; + } + + /** + * Set this repository's name + * + * @param string $name + * + * @return $this + */ + public function setName($name) + { + $this->name = $name; + return $this; + } + + /** + * Return this repository's name + * + * In case no name has been explicitly set yet, the class name is returned. + * + * @return string + */ + public function getName() + { + return $this->name; + } + /** * Set the base DN to use for a user query * @@ -284,6 +348,7 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt $lastModifiedAttribute = 'modifyTimestamp'; } + // TODO(jom): Fetching memberships does not work currently, we'll need some aggregate functionality! $columns = array( 'group' => $this->groupNameAttribute, 'group_name' => $this->groupNameAttribute, From 90d946f149abc57899b4e875e8eedd3b1e66fb38 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 3 Jun 2015 16:40:14 +0200 Subject: [PATCH 13/54] LdapUserGroupBackend: We need a datasource, actually Forgot to add this when disabling LdapRepository inheritance... refs #7343 --- .../UserGroup/LdapUserGroupBackend.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index 1873cdc8e..718006fea 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -99,6 +99,23 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken */ protected $name; + /** + * The datasource being used + * + * @var Connection + */ + protected $ds; + + /** + * Create a new LDAP repository object + * + * @param Connection $ds The data source to use + */ + public function __construct($ds) + { + $this->ds = $ds; + } + /** * Return the given attribute name normed to known LDAP enviroments, if possible * From 7577b37cdb466470373df072ef23cd0d55a0241b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 3 Jun 2015 15:28:36 +0200 Subject: [PATCH 14/54] Change VERSION's format refs #9247 --- application/VERSION | 3 +- application/views/scripts/about/index.phtml | 2 +- library/Icinga/Version.php | 100 ++++---------------- 3 files changed, 21 insertions(+), 84 deletions(-) diff --git a/application/VERSION b/application/VERSION index 47f3a662f..519b667a7 100644 --- a/application/VERSION +++ b/application/VERSION @@ -1,2 +1 @@ -GitCommitID: $Format:%H%d$ -GitCommitDate: $Format:%ci$ +$Format:%H%d %ci$ diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml index 3c37a5e73..53f5a1842 100644 --- a/application/views/scripts/about/index.phtml +++ b/application/views/scripts/about/index.phtml @@ -8,7 +8,7 @@ 'gitCommitID' => $this->translate('Git commit ID: %s'), 'gitCommitDate' => $this->translate('Git commit date: %s') ) as $key => $label) { - if (null !== ($value = $version[$key])) { + if (array_key_exists($key, $version) && null !== ($value = $version[$key])) { $versionInfo[] = sprintf($label, htmlspecialchars($value)); } } diff --git a/library/Icinga/Version.php b/library/Icinga/Version.php index 130e75060..4e5b36945 100644 --- a/library/Icinga/Version.php +++ b/library/Icinga/Version.php @@ -15,91 +15,29 @@ class Version */ public static function get() { - $versionInfo = array( - 'appVersion' => null, - 'gitCommitID' => null, - 'gitCommitDate' => null - ); - - if (false !== ($appVersion = @file( - Icinga::app()->getApplicationDir() . DIRECTORY_SEPARATOR . 'VERSION', - FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES + if (false === ($appVersion = @file_get_contents( + Icinga::app()->getApplicationDir() . DIRECTORY_SEPARATOR . 'VERSION' ))) { - foreach ($appVersion as $av) { - $matches = array(); - if (false === ($res = preg_match( - '/(?\w+)(?:\s*\(.*?(?:(?<=[\(,])\s*tag\s*:\s*v(?P.+?)\s*(?=[\),]).*?)?\))?\s*(?P\S+)/ms', + $appVersion, + $matches + ))) { + throw new IcingaException('Failed at preg_match()'); + } + if ($res === 0) { + return false; } - return $versionInfo; + foreach ($matches as $key => $value) { + if (is_int($key) || $value === '') { + unset($matches[$key]); + } + } + return $matches; } } From 3fd0d99db2e01d6db6faf9c504ed92710fdd008b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 09:57:40 +0200 Subject: [PATCH 15/54] LdapUserGroupBackend: Add support for custom query filters refs #7343 --- .../UserGroup/LdapUserGroupBackend.php | 85 ++++++++++++++++++- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index 718006fea..c0eaa0cb4 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -4,6 +4,7 @@ namespace Icinga\Authentication\UserGroup; use Icinga\Exception\ProgrammingError; +use Icinga\Protocol\Ldap\Expression; use Icinga\Repository\LdapRepository; use Icinga\Repository\RepositoryQuery; use Icinga\User; @@ -59,6 +60,20 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken */ protected $groupMemberAttribute; + /** + * The custom LDAP filter to apply on a user query + * + * @var string + */ + protected $userFilter; + + /** + * The custom LDAP filter to apply on a group query + * + * @var string + */ + protected $groupFilter; + /** * The columns which are not permitted to be queried * @@ -327,6 +342,58 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken return $this->groupMemberAttribute; } + /** + * Set the custom LDAP filter to apply on a user query + * + * @param string $filter + * + * @return $this + */ + public function setUserFilter($filter) + { + if (($filter = trim($filter))) { + $this->userFilter = $filter; + } + + return $this; + } + + /** + * Return the custom LDAP filter to apply on a user query + * + * @return string + */ + public function getUserFilter() + { + return $this->userFilter; + } + + /** + * Set the custom LDAP filter to apply on a group query + * + * @param string $filter + * + * @return $this + */ + public function setGroupFilter($filter) + { + if (($filter = trim($filter))) { + $this->groupFilter = $filter; + } + + return $this; + } + + /** + * Return the custom LDAP filter to apply on a group query + * + * @return string + */ + public function getGroupFilter() + { + return $this->groupFilter; + } + /** * Return a new query for the given columns * @@ -338,6 +405,11 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken { $query = parent::select($columns); $query->getQuery()->setBase($this->groupBaseDn); + if ($this->groupFilter) { + // TODO(jom): This should differentiate between groups and their memberships + $query->getQuery()->where(new Expression($this->groupFilter)); + } + return $query; } @@ -430,15 +502,17 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken */ public function getMemberships(User $user) { - $userDn = $this->ds + $userQuery = $this->ds ->select() ->from($this->userClass) ->where($this->userNameAttribute, $user->getUsername()) ->setBase($this->userBaseDn) - ->setUsePagedResults(false) - ->fetchDn(); + ->setUsePagedResults(false); + if ($this->userFilter) { + $userQuery->where(new Expression($this->userFilter)); + } - if ($userDn === null) { + if (($userDn = $userQuery->fetchDn()) === null) { return array(); } @@ -447,6 +521,9 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken ->from($this->groupClass, array($this->groupNameAttribute)) ->where($this->groupMemberAttribute, $userDn) ->setBase($this->groupBaseDn); + if ($this->groupFilter) { + $groupQuery->where(new Expression($this->groupFilter)); + } $groups = array(); foreach ($groupQuery as $row) { From 602f0cf7556489675dbf488ca429898415f5f383 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 10:18:24 +0200 Subject: [PATCH 16/54] Move Version to Icinga\Application\ refs #9247 --- application/controllers/AboutController.php | 2 +- library/Icinga/{ => Application}/Version.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) rename library/Icinga/{ => Application}/Version.php (95%) diff --git a/application/controllers/AboutController.php b/application/controllers/AboutController.php index 376490443..a4247f234 100644 --- a/application/controllers/AboutController.php +++ b/application/controllers/AboutController.php @@ -4,7 +4,7 @@ # namespace Icinga\Application\Controllers; use Icinga\Web\Controller\ActionController; -use Icinga\Version; +use Icinga\Application\Version; class AboutController extends ActionController { diff --git a/library/Icinga/Version.php b/library/Icinga/Application/Version.php similarity index 95% rename from library/Icinga/Version.php rename to library/Icinga/Application/Version.php index 4e5b36945..0c49330ae 100644 --- a/library/Icinga/Version.php +++ b/library/Icinga/Application/Version.php @@ -1,9 +1,8 @@ Date: Fri, 5 Jun 2015 10:19:28 +0200 Subject: [PATCH 17/54] LdapUserGroupBackend: Let the backend decide which defaults to use refs #7343 --- .../UserGroup/LdapUserGroupBackend.php | 62 +++++++++++++++++++ .../UserGroup/UserGroupBackend.php | 19 +----- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index c0eaa0cb4..d2387b7b4 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -3,6 +3,7 @@ namespace Icinga\Authentication\UserGroup; +use Icinga\Data\ConfigObject; use Icinga\Exception\ProgrammingError; use Icinga\Protocol\Ldap\Expression; use Icinga\Repository\LdapRepository; @@ -532,4 +533,65 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken return $groups; } + + /** + * Apply the given configuration on this backend + * + * @param ConfigObject $config + * + * @return $this + */ + public function setConfig(ConfigObject $config) + { + if ($config->backend === 'ldap') { + $defaults = $this->getOpenLdapDefaults(); + } elseif ($config->backend === 'msldap') { + $defaults = $this->getActiveDirectoryDefaults(); + } else { + $defaults = new ConfigObject(); + } + + return $this + ->setGroupBaseDn($config->base_dn) + ->setUserBaseDn($config->get('user_base_dn', $this->getGroupBaseDn())) + ->setGroupClass($config->get('group_class', $defaults->group_class)) + ->setUserClass($config->get('user_class', $defaults->user_class)) + ->setGroupNameAttribute($config->get('group_name_attribute', $defaults->group_name_attribute)) + ->setUserNameAttribute($config->get('user_name_attribute', $defaults->user_name_attribute)) + ->setGroupMemberAttribute($config->get('group_member_attribute', $defaults->group_member_attribute)) + ->setGroupFilter($config->filter) + ->setUserFilter($config->user_filter); + } + + /** + * Return the configuration defaults for an OpenLDAP environment + * + * @return ConfigObject + */ + protected function getOpenLdapDefaults() + { + return new ConfigObject(array( + 'group_class' => 'group', + 'user_class' => 'inetOrgPerson', + 'group_name_attribute' => 'gid', + 'user_name_attribute' => 'uid', + 'group_member_attribute' => 'member' + )); + } + + /** + * Return the configuration defaults for an ActiveDirectory environment + * + * @return ConfigObject + */ + protected function getActiveDirectoryDefaults() + { + return new ConfigObject(array( + 'group_class' => 'group', + 'user_class' => 'user', + 'group_name_attribute' => 'sAMAccountName', + 'user_name_attribute' => 'sAMAccountName', + 'group_member_attribute' => 'member' + )); + } } diff --git a/library/Icinga/Authentication/UserGroup/UserGroupBackend.php b/library/Icinga/Authentication/UserGroup/UserGroupBackend.php index 585f7c449..978860a37 100644 --- a/library/Icinga/Authentication/UserGroup/UserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/UserGroupBackend.php @@ -159,26 +159,9 @@ class UserGroupBackend $backend = new IniUserGroupBackend($resource); break; case 'ldap': - $backend = new LdapUserGroupBackend($resource); - $backend - ->setGroupBaseDn($backendConfig->base_dn) - ->setUserBaseDn($backendConfig->get('user_base_dn', $backend->getGroupBaseDn())) - ->setGroupClass($backendConfig->get('group_class', 'group')) - ->setUserClass($backendConfig->get('user_class', 'inetOrgPerson')) - ->setGroupNameAttribute($backendConfig->get('group_name_attribute', 'gid')) - ->setUserNameAttribute($backendConfig->get('user_name_attribute', 'uid')) - ->setGroupMemberAttribute($backendConfig->get('group_member_attribute', 'member')); - break; case 'msldap': $backend = new LdapUserGroupBackend($resource); - $backend - ->setGroupBaseDn($backendConfig->base_dn) - ->setUserBaseDn($backendConfig->get('user_base_dn', $backend->getGroupBaseDn())) - ->setGroupClass($backendConfig->get('group_class', 'group')) - ->setUserClass($backendConfig->get('user_class', 'user')) - ->setGroupNameAttribute($backendConfig->get('group_name_attribute', 'sAMAccountName')) - ->setUserNameAttribute($backendConfig->get('user_name_attribute', $backend->getGroupNameAttribute())) - ->setGroupMemberAttribute($backendConfig->get('group_member_attribute', 'member')); + $backend->setConfig($backendConfig); break; } From cbc731034a20b6f4860ec5e70887f4d98fe5f0a9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 10:23:17 +0200 Subject: [PATCH 18/54] Don't throw any exception refs #9247 --- library/Icinga/Application/Version.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/library/Icinga/Application/Version.php b/library/Icinga/Application/Version.php index 0c49330ae..65b6138d9 100644 --- a/library/Icinga/Application/Version.php +++ b/library/Icinga/Application/Version.php @@ -3,8 +3,6 @@ namespace Icinga\Application; -use Icinga\Exception\IcingaException; - class Version { /** @@ -25,10 +23,7 @@ class Version '/(?\w+)(?:\s*\(.*?(?:(?<=[\(,])\s*tag\s*:\s*v(?P.+?)\s*(?=[\),]).*?)?\))?\s*(?P\S+)/ms', $appVersion, $matches - ))) { - throw new IcingaException('Failed at preg_match()'); - } - if ($res === 0) { + )) || $res === 0) { return false; } From 127489ca20cc22255024e6c017220fc28f983c84 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 10:40:47 +0200 Subject: [PATCH 19/54] UserBackend: Allow to only pass a backend's name --- library/Icinga/Authentication/User/UserBackend.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Authentication/User/UserBackend.php b/library/Icinga/Authentication/User/UserBackend.php index 42a4c2b42..7afd561a7 100644 --- a/library/Icinga/Authentication/User/UserBackend.php +++ b/library/Icinga/Authentication/User/UserBackend.php @@ -3,6 +3,7 @@ namespace Icinga\Authentication\User; +use Icinga\Application\Config; use Icinga\Application\Logger; use Icinga\Application\Icinga; use Icinga\Data\ConfigObject; @@ -106,8 +107,17 @@ class UserBackend * * @throws ConfigurationError */ - public static function create($name, ConfigObject $backendConfig) + public static function create($name, ConfigObject $backendConfig = null) { + if ($backendConfig === null) { + $authConfig = Config::app('authentication'); + if ($authConfig->hasSection($name)) { + $backendConfig = $authConfig->getSection($name); + } else { + throw new ConfigurationError('User backend "%s" does not exist', $name); + } + } + if ($backendConfig->name !== null) { $name = $backendConfig->name; } From 0ab192cd1f75a90a1e23e913a2819e5429afb602 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 10:41:47 +0200 Subject: [PATCH 20/54] LdapUserGroupBackend: Allow to link a user backend refs #7343 --- .../UserGroup/LdapUserGroupBackend.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index d2387b7b4..42d67a0e8 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -3,7 +3,10 @@ namespace Icinga\Authentication\UserGroup; +use Icinga\Authentication\User\UserBackend; +use Icinga\Authentication\User\LdapUserBackend; use Icinga\Data\ConfigObject; +use Icinga\Exception\ConfigurationError; use Icinga\Exception\ProgrammingError; use Icinga\Protocol\Ldap\Expression; use Icinga\Repository\LdapRepository; @@ -540,6 +543,8 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken * @param ConfigObject $config * * @return $this + * + * @throws ConfigurationError In case a linked user backend does not exist or is not a LdapUserBackend */ public function setConfig(ConfigObject $config) { @@ -551,6 +556,20 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken $defaults = new ConfigObject(); } + if ($config->user_backend) { + $userBackend = UserBackend::create($config->user_backend); + if (! $userBackend instanceof LdapUserBackend) { + throw new ConfigurationError('User backend "%s" is not of type LDAP', $config->user_backend); + } + + $defaults->merge(array( + 'user_base_dn' => $userBackend->getBaseDn(), + 'user_class' => $userBackend->getUserClass(), + 'user_name_attribute' => $userBackend->getUserNameAttribute(), + 'user_filter' => $userBackend->getFilter() + )); + } + return $this ->setGroupBaseDn($config->base_dn) ->setUserBaseDn($config->get('user_base_dn', $this->getGroupBaseDn())) From 02d2ea682e0774cbe072626fd7dd412d19457e6f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 10:51:54 +0200 Subject: [PATCH 21/54] LdapUserGroupBackend: Do not permit to link different directories I cannot think of a valid usecase right now. In case someone got one, revert this commit and make use of the backend itself and not only its configuration. refs #7343 --- .../UserGroup/LdapUserGroupBackend.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index 42d67a0e8..a51df856d 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -544,7 +544,7 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken * * @return $this * - * @throws ConfigurationError In case a linked user backend does not exist or is not a LdapUserBackend + * @throws ConfigurationError In case a linked user backend does not exist or is invalid */ public function setConfig(ConfigObject $config) { @@ -562,6 +562,17 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken throw new ConfigurationError('User backend "%s" is not of type LDAP', $config->user_backend); } + if ( + $this->ds->getHostname() !== $userBackend->getDataSource()->getHostname() + || $this->ds->getPort() !== $userBackend->getDataSource()->getPort() + ) { + // TODO(jom): Elaborate whether it makes sense to link directories on different hosts + throw new ConfigurationError( + 'It is required that a linked user backend refers to the ' + . 'same directory as it\'s user group backend counterpart' + ); + } + $defaults->merge(array( 'user_base_dn' => $userBackend->getBaseDn(), 'user_class' => $userBackend->getUserClass(), From cacd97fb4665284e325a4ee287152b1bf1b999d6 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 11:09:31 +0200 Subject: [PATCH 22/54] LdapUserGroupBackend: Make default configuration providers public I'd like to access these when preparing a config form. refs #7343 --- .../Icinga/Authentication/UserGroup/LdapUserGroupBackend.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index a51df856d..60b94ec27 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -598,7 +598,7 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken * * @return ConfigObject */ - protected function getOpenLdapDefaults() + public function getOpenLdapDefaults() { return new ConfigObject(array( 'group_class' => 'group', @@ -614,7 +614,7 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken * * @return ConfigObject */ - protected function getActiveDirectoryDefaults() + public function getActiveDirectoryDefaults() { return new ConfigObject(array( 'group_class' => 'group', From 67322e0b364d6ff4d33a35e77ff2890299fbc035 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 11:56:15 +0200 Subject: [PATCH 23/54] Show an error message rather than just "unknown version" refs #9247 --- application/views/scripts/about/index.phtml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml index 53f5a1842..6a01e1bf9 100644 --- a/application/views/scripts/about/index.phtml +++ b/application/views/scripts/about/index.phtml @@ -13,9 +13,13 @@ } } } + + echo ( + 0 === count($versionInfo) + ? '

    ' . $this->translate( + 'Can\'t determine Icinga Web 2\'s version' + ) + : '

    ' . nl2br(implode("\n", $versionInfo), false) + ) . '

    '; ?> -

    translate('unknown version') - : nl2br(implode("\n", $versionInfo), false) - ?>

    From 0a5dcd2c8f5340e76c66c058d3bbee0441e27930 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 12:03:23 +0200 Subject: [PATCH 24/54] Include VERSION in the RPM package refs #9247 --- icingaweb2.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/icingaweb2.spec b/icingaweb2.spec index 1ea69451d..c7aa4cb8d 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -218,6 +218,7 @@ rm -rf %{buildroot} %{basedir}/application/forms %{basedir}/application/layouts %{basedir}/application/views +%{basedir}/application/VERSION %{basedir}/doc %{basedir}/modules %{basedir}/public From 6b14195809ce51bdf6f1c803af165438e948bd72 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 12:38:55 +0200 Subject: [PATCH 25/54] Rename .fileNotReadable to .message-error refs #9247 --- public/css/icinga/layout-structure.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 3a8713a38..5b5eee5ef 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -331,8 +331,7 @@ html { position: absolute; } -/* TODO: replace this with .error */ -.fileNotReadable { +.message-error { padding: 0.5em; background-color: @colorCritical; font-weight: bold; From 7ebf185036a7bf00eeb65e40859634796e7f327c Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 13:07:16 +0200 Subject: [PATCH 26/54] UserGroupBackendForm: Fix that autosubmit do not have any effect refs #7343 --- .../Config/UserGroup/UserGroupBackendForm.php | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/application/forms/Config/UserGroup/UserGroupBackendForm.php b/application/forms/Config/UserGroup/UserGroupBackendForm.php index dd9875c9a..abcf1eaa4 100644 --- a/application/forms/Config/UserGroup/UserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/UserGroupBackendForm.php @@ -13,6 +13,13 @@ use Icinga\Forms\ConfigForm; */ class UserGroupBackendForm extends ConfigForm { + /** + * The backend to load when displaying the form for the first time + * + * @var string + */ + protected $backendToLoad; + /** * Initialize this form */ @@ -53,10 +60,7 @@ class UserGroupBackendForm extends ConfigForm throw new NotFoundError('No user group backend called "%s" found', $name); } - $data = $this->config->getSection($name)->toArray(); - $data['type'] = $data['backend']; - $data['name'] = $name; - $this->populate($data); + $this->backendToLoad = $name; return $this; } @@ -187,7 +191,8 @@ class UserGroupBackendForm extends ConfigForm 'autosubmit' => true, 'label' => $this->translate('Backend Type'), 'description' => $this->translate('The type of this user group backend'), - 'multiOptions' => $backendTypes + 'multiOptions' => $backendTypes, + 'value' => $backendType ) ); @@ -195,4 +200,17 @@ class UserGroupBackendForm extends ConfigForm $backendForm->createElements($formData); $this->addElements($backendForm->getElements()); } + + /** + * Populate the configuration of the backend to load + */ + public function onRequest() + { + if ($this->backendToLoad) { + $data = $this->config->getSection($this->backendToLoad)->toArray(); + $data['type'] = $data['backend']; + $data['name'] = $this->backendToLoad; + $this->populate($data); + } + } } From 447088af22319efc5d51fc5546644ce1488b8e90 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 14:52:46 +0200 Subject: [PATCH 27/54] Introduce form LdapUserGroupBackendForm refs #7343 --- .../UserGroup/LdapUserGroupBackendForm.php | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 application/forms/Config/UserGroup/LdapUserGroupBackendForm.php diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php new file mode 100644 index 000000000..71af892f2 --- /dev/null +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -0,0 +1,304 @@ +setName('form_config_ldapusergroupbackend'); + } + + /** + * Create and add elements to this form + * + * @param array $formData + */ + public function createElements(array $formData) + { + $resourceNames = $this->getLdapResourceNames(); + $this->addElement( + 'select', + 'resource', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => $this->translate('LDAP Connection'), + 'description' => $this->translate('The LDAP connection to use for this backend.'), + 'multiOptions' => array_combine($resourceNames, $resourceNames) + ) + ); + $resource = ResourceFactory::create( + isset($formData['resource']) && in_array($formData['resource'], $resourceNames) + ? $formData['resource'] + : $resourceNames[0] + ); + + $userBackends = array('none' => $this->translate('None', 'usergroupbackend.ldap.user_backend')); + $userBackendNames = $this->getLdapUserBackendNames($resource); + if (! empty($userBackendNames)) { + $userBackends = array_merge($userBackends, array_combine($userBackendNames, $userBackendNames)); + } + $this->addElement( + 'select', + 'user_backend', + array( + 'required' => true, + 'autosubmit' => true, + 'label' => $this->translate('User Backend'), + 'description' => $this->translate('The user backend to link with this user group backend.'), + 'multiOptions' => $userBackends + ) + ); + + $groupBackend = new LdapUserGroupBackend($resource); + if ($formData['type'] === 'ldap') { + $defaults = $groupBackend->getOpenLdapDefaults(); + $disabled = null; // MUST BE null, do NOT change this to false! + } else { // $formData['type'] === 'msldap' + $defaults = $groupBackend->getActiveDirectoryDefaults(); + $disabled = true; + } + + if (isset($formData['user_backend']) && $formData['user_backend'] !== 'none') { + $userBackend = UserBackend::create($formData['user_backend']); + $defaults->merge(array( + 'user_base_dn' => $userBackend->getBaseDn(), + 'user_class' => $userBackend->getUserClass(), + 'user_name_attribute' => $userBackend->getUserNameAttribute(), + 'user_filter' => $userBackend->getFilter() + )); + $disabled = true; + } + + $this->createGroupConfigElements($defaults, $disabled); + $this->createUserConfigElements($defaults, $disabled); + } + + /** + * Create and add all elements to this form required for the group configuration + * + * @param ConfigObject $defaults + * @param null|bool $disabled + */ + protected function createGroupConfigElements(ConfigObject $defaults, $disabled) + { + $this->addElement( + 'text', + 'group_class', + array( + 'preserveDefault' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP Group Object Class'), + 'description' => $this->translate('The object class used for storing groups on the LDAP server.'), + 'value' => $defaults->group_class + ) + ); + $this->addElement( + 'text', + 'group_filter', + array( + 'preserveDefault' => true, + 'allowEmpty' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP Group Filter'), + 'description' => $this->translate( + 'An additional filter to use when looking up groups using the specified connection. ' + . 'Leave empty to not to use any additional filter rules.' + ), + 'requirement' => $this->translate( + 'The filter needs to be expressed as standard LDAP expression, without' + . ' outer parentheses. (e.g. &(foo=bar)(bar=foo) or foo=bar)' + ), + 'validators' => array( + array( + 'Callback', + false, + array( + 'callback' => function ($v) { + return strpos($v, '(') !== 0; + }, + 'messages' => array( + 'callbackValue' => $this->translate('The filter must not be wrapped in parantheses.') + ) + ) + ) + ), + 'value' => $defaults->group_filter + ) + ); + $this->addElement( + 'text', + 'group_name_attribute', + array( + 'preserveDefault' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP Group Name Attribute'), + 'description' => $this->translate( + 'The attribute name used for storing a group\'s name on the LDAP server.' + ), + 'value' => $defaults->group_name_attribute + ) + ); + $this->addElement( + 'text', + 'base_dn', + array( + 'preserveDefault' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP Group Base DN'), + 'description' => $this->translate( + 'The path where groups can be found on the LDAP server. Leave ' . + 'empty to select all users available using the specified connection.' + ), + 'value' => $defaults->base_dn + ) + ); + } + + /** + * Create and add all elements to this form required for the user configuration + * + * @param ConfigObject $defaults + * @param null|bool $disabled + */ + protected function createUserConfigElements(ConfigObject $defaults, $disabled) + { + $this->addElement( + 'text', + 'user_class', + array( + 'preserveDefault' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP User Object Class'), + 'description' => $this->translate('The object class used for storing users on the LDAP server.'), + 'value' => $defaults->user_class + ) + ); + $this->addElement( + 'text', + 'user_filter', + array( + 'preserveDefault' => true, + 'allowEmpty' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP User Filter'), + 'description' => $this->translate( + 'An additional filter to use when looking up users using the specified connection. ' + . 'Leave empty to not to use any additional filter rules.' + ), + 'requirement' => $this->translate( + 'The filter needs to be expressed as standard LDAP expression, without' + . ' outer parentheses. (e.g. &(foo=bar)(bar=foo) or foo=bar)' + ), + 'validators' => array( + array( + 'Callback', + false, + array( + 'callback' => function ($v) { + return strpos($v, '(') !== 0; + }, + 'messages' => array( + 'callbackValue' => $this->translate('The filter must not be wrapped in parantheses.') + ) + ) + ) + ), + 'value' => $defaults->user_filter + ) + ); + $this->addElement( + 'text', + 'user_name_attribute', + array( + 'preserveDefault' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP User Name Attribute'), + 'description' => $this->translate( + 'The attribute name used for storing a user\'s name on the LDAP server.' + ), + 'value' => $defaults->user_name_attribute + ) + ); + $this->addElement( + 'text', + 'user_base_dn', + array( + 'preserveDefault' => true, + 'disabled' => $disabled, + 'label' => $this->translate('LDAP User Base DN'), + 'description' => $this->translate( + 'The path where users can be found on the LDAP server. Leave ' . + 'empty to select all users available using the specified connection.' + ), + 'value' => $defaults->user_base_dn + ) + ); + } + + /** + * Return the names of all configured LDAP resources + * + * @return array + */ + protected function getLdapResourceNames() + { + $names = array(); + foreach (ResourceFactory::getResourceConfigs() as $name => $config) { + if (in_array(strtolower($config->type), array('ldap', 'msldap'))) { + $names[] = $name; + } + } + + if (empty($names)) { + Notification::error( + $this->translate('No LDAP resources available. Please configure an LDAP resource first.') + ); + $this->getResponse()->redirectAndExit('config/createresource'); + } + + return $names; + } + + /** + * Return the names of all configured LDAP user backends + * + * @param Connection $resource + * + * @return array + */ + protected function getLdapUserBackendNames(Connection $resource) + { + $names = array(); + foreach (Config::app('authentication') as $name => $config) { + if (in_array(strtolower($config->backend), array('ldap', 'msldap'))) { + $backendResource = ResourceFactory::create($config->resource); + if ( + $backendResource->getHostname() === $resource->getHostname() + && $backendResource->getPort() === $resource->getPort() + ) { + $names[] = $name; + } + } + } + + return $names; + } +} From 5688f0cb857ce216dc831c747978c606ac1a2696 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 14:53:29 +0200 Subject: [PATCH 28/54] Allow to configure user group backends of type LDAP refs #7343 --- .../Config/UserGroup/UserGroupBackendForm.php | 41 ++++++++++++++----- .../UserGroup/LdapUserGroupBackend.php | 2 +- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/application/forms/Config/UserGroup/UserGroupBackendForm.php b/application/forms/Config/UserGroup/UserGroupBackendForm.php index abcf1eaa4..68b3d2816 100644 --- a/application/forms/Config/UserGroup/UserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/UserGroupBackendForm.php @@ -38,10 +38,17 @@ class UserGroupBackendForm extends ConfigForm */ public function getBackendForm($type) { - if ($type === 'db') { - return new DbUserGroupBackendForm(); - } else { - throw new InvalidArgumentException(sprintf($this->translate('Invalid backend type "%s" provided'), $type)); + switch ($type) + { + case 'db': + return new DbUserGroupBackendForm(); + case 'ldap': + case 'msldap': + return new LdapUserGroupBackendForm(); + default: + throw new InvalidArgumentException( + sprintf($this->translate('Invalid backend type "%s" provided'), $type) + ); } } @@ -165,7 +172,9 @@ class UserGroupBackendForm extends ConfigForm // TODO(jom): We did not think about how to configure custom group backends yet! $backendTypes = array( - 'db' => $this->translate('Database') + 'db' => $this->translate('Database'), + 'ldap' => $this->translate('LDAP'), + 'msldap' => $this->translate('ActiveDirectory') ); $backendType = isset($formData['type']) ? $formData['type'] : null; @@ -191,14 +200,11 @@ class UserGroupBackendForm extends ConfigForm 'autosubmit' => true, 'label' => $this->translate('Backend Type'), 'description' => $this->translate('The type of this user group backend'), - 'multiOptions' => $backendTypes, - 'value' => $backendType + 'multiOptions' => $backendTypes ) ); - $backendForm = $this->getBackendForm($backendType); - $backendForm->createElements($formData); - $this->addElements($backendForm->getElements()); + $this->addSubForm($this->getBackendForm($backendType)->create($formData), 'backend_form'); } /** @@ -213,4 +219,19 @@ class UserGroupBackendForm extends ConfigForm $this->populate($data); } } + + /** + * Retrieve all form element values + * + * @param bool $suppressArrayNotation Ignored + * + * @return array + */ + public function getValues($suppressArrayNotation = false) + { + $values = parent::getValues(); + $values = array_merge($values, $values['backend_form']); + unset($values['backend_form']); + return $values; + } } diff --git a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php index 60b94ec27..224013e9f 100644 --- a/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php @@ -556,7 +556,7 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken $defaults = new ConfigObject(); } - if ($config->user_backend) { + if ($config->user_backend && $config->user_backend !== 'none') { $userBackend = UserBackend::create($config->user_backend); if (! $userBackend instanceof LdapUserBackend) { throw new ConfigurationError('User backend "%s" is not of type LDAP', $config->user_backend); From 6dedb48444701d65251a1b4427374edf8ef0a795 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 15:37:31 +0200 Subject: [PATCH 29/54] Indicate that nothing was found in the docs when searching refs #8847 --- modules/doc/application/views/scripts/search/index.phtml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/doc/application/views/scripts/search/index.phtml b/modules/doc/application/views/scripts/search/index.phtml index 3311c47a1..3d220392e 100644 --- a/modules/doc/application/views/scripts/search/index.phtml +++ b/modules/doc/application/views/scripts/search/index.phtml @@ -1,8 +1,8 @@
    $search): ?> - isEmpty()): ?> -

    escape($title) ?>

    - - +

    escape($title) ?>

    + isEmpty() + ? $this->translate('No documentation found matching the filter') + : $search ?>
    From e9e97cb1b377ff94fae2bb4281354c88bcb4e7d9 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 15:41:24 +0200 Subject: [PATCH 30/54] UserGroupBackendForm: Unset the `name' property, always refs #7343 --- .../forms/Config/UserGroup/UserGroupBackendForm.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/application/forms/Config/UserGroup/UserGroupBackendForm.php b/application/forms/Config/UserGroup/UserGroupBackendForm.php index 68b3d2816..e8439ccaf 100644 --- a/application/forms/Config/UserGroup/UserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/UserGroupBackendForm.php @@ -114,9 +114,12 @@ class UserGroupBackendForm extends ConfigForm } $backendConfig = $this->config->getSection($name); - if (isset($data['name']) && $data['name'] !== $name) { - $this->config->removeSection($name); - $name = $data['name']; + if (isset($data['name'])) { + if ($data['name'] !== $name) { + $this->config->removeSection($name); + $name = $data['name']; + } + unset($data['name']); } From e6837cf9e9612c3d837090eabb6be9505f851392 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 15:42:35 +0200 Subject: [PATCH 31/54] UsergroupbackendController: Do not persist empty configuration values refs #7343 --- application/controllers/UsergroupbackendController.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/application/controllers/UsergroupbackendController.php b/application/controllers/UsergroupbackendController.php index cdb6826be..1a3c2f46e 100644 --- a/application/controllers/UsergroupbackendController.php +++ b/application/controllers/UsergroupbackendController.php @@ -52,7 +52,7 @@ class UsergroupbackendController extends Controller $form->setIniConfig(Config::app('groups')); $form->setOnSuccess(function (UserGroupBackendForm $form) { try { - $form->add($form->getValues()); + $form->add(array_filter($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -85,7 +85,12 @@ class UsergroupbackendController extends Controller $form->setIniConfig(Config::app('groups')); $form->setOnSuccess(function (UserGroupBackendForm $form) use ($backendName) { try { - $form->edit($backendName, $form->getValues()); + $form->edit($backendName, array_map( + function ($v) { + return $v !== '' ? $v : null; + }, + $form->getValues() + )); } catch (Exception $e) { $form->error($e->getMessage()); return false; From 975edbe548ecacabee22fb871e21dd2b8312dc19 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 15:45:33 +0200 Subject: [PATCH 32/54] UserGroupBackendForm: Do not persist null values, really Revert this once #9376 has been fixed. refs #9376 --- .../forms/Config/UserGroup/UserGroupBackendForm.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/application/forms/Config/UserGroup/UserGroupBackendForm.php b/application/forms/Config/UserGroup/UserGroupBackendForm.php index e8439ccaf..cfd615e47 100644 --- a/application/forms/Config/UserGroup/UserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/UserGroupBackendForm.php @@ -123,7 +123,14 @@ class UserGroupBackendForm extends ConfigForm unset($data['name']); } - $this->config->setSection($name, $backendConfig->merge($data)); + $backendConfig->merge($data); + foreach ($backendConfig->toArray() as $k => $v) { + if ($v === null) { + unset($backendConfig->$k); + } + } + + $this->config->setSection($name, $backendConfig); return $this; } From 05b7c3aa6a57b355687ed94784e2a6d5e1e14d8a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 16:01:37 +0200 Subject: [PATCH 33/54] Replace

    w/

    refs #8847 --- modules/doc/application/views/scripts/search/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/application/views/scripts/search/index.phtml b/modules/doc/application/views/scripts/search/index.phtml index 3d220392e..c613f04df 100644 --- a/modules/doc/application/views/scripts/search/index.phtml +++ b/modules/doc/application/views/scripts/search/index.phtml @@ -1,6 +1,6 @@
    $search): ?> -

    escape($title) ?>

    +

    escape($title) ?>

    isEmpty() ? $this->translate('No documentation found matching the filter') : $search ?> From 24cb6bfc6e8ec3f9712e07524224670d8faebd64 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 16:01:52 +0200 Subject: [PATCH 34/54] Form: Preserve defaults for subforms as well --- library/Icinga/Web/Form.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 48f7acdb7..08cb02388 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -855,8 +855,19 @@ class Form extends Zend_Form public function populate(array $defaults) { $this->create($defaults); + $this->preserveDefaults($this, $defaults); + return parent::populate($defaults); + } - foreach ($this->getElements() as $name => $_) { + /** + * Recurse the given form and unset all unchanged default values + * + * @param Zend_Form $form + * @param array $defaults + */ + protected function preserveDefaults(Zend_Form $form, array & $defaults) + { + foreach ($form->getElements() as $name => $_) { if ( array_key_exists($name, $defaults) && array_key_exists($name . static::DEFAULT_SUFFIX, $defaults) @@ -866,7 +877,9 @@ class Form extends Zend_Form } } - return parent::populate($defaults); + foreach ($form->getSubForms() as $_ => $subForm) { + $this->preserveDefaults($subForm, $defaults); + } } /** From 797e9de6297007b8af806c41d47be2bf443db43f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 16:07:27 +0200 Subject: [PATCH 35/54] LdapUserGroupBackendForm: Do not disable group configuration.. ..when no user backend has been linked and it's not a ActiveDirectory. refs #7343 --- .../Config/UserGroup/LdapUserGroupBackendForm.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 71af892f2..3e97f4dd4 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -70,10 +70,10 @@ class LdapUserGroupBackendForm extends Form $groupBackend = new LdapUserGroupBackend($resource); if ($formData['type'] === 'ldap') { $defaults = $groupBackend->getOpenLdapDefaults(); - $disabled = null; // MUST BE null, do NOT change this to false! + $groupConfigDisabled = $userConfigDisabled = null; // MUST BE null, do NOT change this to false! } else { // $formData['type'] === 'msldap' $defaults = $groupBackend->getActiveDirectoryDefaults(); - $disabled = true; + $groupConfigDisabled = $userConfigDisabled = true; } if (isset($formData['user_backend']) && $formData['user_backend'] !== 'none') { @@ -84,11 +84,11 @@ class LdapUserGroupBackendForm extends Form 'user_name_attribute' => $userBackend->getUserNameAttribute(), 'user_filter' => $userBackend->getFilter() )); - $disabled = true; + $userConfigDisabled = true; } - $this->createGroupConfigElements($defaults, $disabled); - $this->createUserConfigElements($defaults, $disabled); + $this->createGroupConfigElements($defaults, $groupConfigDisabled); + $this->createUserConfigElements($defaults, $userConfigDisabled); } /** From e910a5a254ba834d7db01925d0e259d21b57ced3 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 16:16:35 +0200 Subject: [PATCH 36/54] LdapUserGroupBackendForm: Do not disable `base_dn' and `user_base_dn' refs #7343 --- .../Config/UserGroup/LdapUserGroupBackendForm.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 3e97f4dd4..5b6d42b89 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -76,6 +76,7 @@ class LdapUserGroupBackendForm extends Form $groupConfigDisabled = $userConfigDisabled = true; } + $dnDisabled = null; // MUST BE null if (isset($formData['user_backend']) && $formData['user_backend'] !== 'none') { $userBackend = UserBackend::create($formData['user_backend']); $defaults->merge(array( @@ -84,11 +85,11 @@ class LdapUserGroupBackendForm extends Form 'user_name_attribute' => $userBackend->getUserNameAttribute(), 'user_filter' => $userBackend->getFilter() )); - $userConfigDisabled = true; + $userConfigDisabled = $dnDisabled = true; } $this->createGroupConfigElements($defaults, $groupConfigDisabled); - $this->createUserConfigElements($defaults, $userConfigDisabled); + $this->createUserConfigElements($defaults, $userConfigDisabled, $dnDisabled); } /** @@ -161,7 +162,6 @@ class LdapUserGroupBackendForm extends Form 'base_dn', array( 'preserveDefault' => true, - 'disabled' => $disabled, 'label' => $this->translate('LDAP Group Base DN'), 'description' => $this->translate( 'The path where groups can be found on the LDAP server. Leave ' . @@ -177,8 +177,9 @@ class LdapUserGroupBackendForm extends Form * * @param ConfigObject $defaults * @param null|bool $disabled + * @param null|bool $dnDisabled */ - protected function createUserConfigElements(ConfigObject $defaults, $disabled) + protected function createUserConfigElements(ConfigObject $defaults, $disabled, $dnDisabled) { $this->addElement( 'text', @@ -242,7 +243,7 @@ class LdapUserGroupBackendForm extends Form 'user_base_dn', array( 'preserveDefault' => true, - 'disabled' => $disabled, + 'disabled' => $dnDisabled, 'label' => $this->translate('LDAP User Base DN'), 'description' => $this->translate( 'The path where users can be found on the LDAP server. Leave ' . From 840069b436437920e66db2b93134921d76eb9769 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 16:34:28 +0200 Subject: [PATCH 37/54] LdapUserGroupBackendForm: Do not persist default values ... refs #7343 --- .../forms/Config/UserGroup/LdapUserGroupBackendForm.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 5b6d42b89..1ce6af713 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -105,6 +105,7 @@ class LdapUserGroupBackendForm extends Form 'group_class', array( 'preserveDefault' => true, + 'ignore' => $disabled, 'disabled' => $disabled, 'label' => $this->translate('LDAP Group Object Class'), 'description' => $this->translate('The object class used for storing groups on the LDAP server.'), @@ -117,6 +118,7 @@ class LdapUserGroupBackendForm extends Form array( 'preserveDefault' => true, 'allowEmpty' => true, + 'ignore' => $disabled, 'disabled' => $disabled, 'label' => $this->translate('LDAP Group Filter'), 'description' => $this->translate( @@ -149,6 +151,7 @@ class LdapUserGroupBackendForm extends Form 'group_name_attribute', array( 'preserveDefault' => true, + 'ignore' => $disabled, 'disabled' => $disabled, 'label' => $this->translate('LDAP Group Name Attribute'), 'description' => $this->translate( @@ -186,6 +189,7 @@ class LdapUserGroupBackendForm extends Form 'user_class', array( 'preserveDefault' => true, + 'ignore' => $disabled, 'disabled' => $disabled, 'label' => $this->translate('LDAP User Object Class'), 'description' => $this->translate('The object class used for storing users on the LDAP server.'), @@ -198,6 +202,7 @@ class LdapUserGroupBackendForm extends Form array( 'preserveDefault' => true, 'allowEmpty' => true, + 'ignore' => $disabled, 'disabled' => $disabled, 'label' => $this->translate('LDAP User Filter'), 'description' => $this->translate( @@ -230,6 +235,7 @@ class LdapUserGroupBackendForm extends Form 'user_name_attribute', array( 'preserveDefault' => true, + 'ignore' => $disabled, 'disabled' => $disabled, 'label' => $this->translate('LDAP User Name Attribute'), 'description' => $this->translate( @@ -243,6 +249,7 @@ class LdapUserGroupBackendForm extends Form 'user_base_dn', array( 'preserveDefault' => true, + 'ignore' => $dnDisabled, 'disabled' => $dnDisabled, 'label' => $this->translate('LDAP User Base DN'), 'description' => $this->translate( From c800f1e6aa6ce4f7daaa5c244c49ac52152b4153 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 16:40:17 +0200 Subject: [PATCH 38/54] UserGroupBackendForm: Do not translate LDAP and ActiveDirectory refs #7343 --- application/forms/Config/UserGroup/UserGroupBackendForm.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/forms/Config/UserGroup/UserGroupBackendForm.php b/application/forms/Config/UserGroup/UserGroupBackendForm.php index cfd615e47..5bd0d9681 100644 --- a/application/forms/Config/UserGroup/UserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/UserGroupBackendForm.php @@ -183,8 +183,8 @@ class UserGroupBackendForm extends ConfigForm // TODO(jom): We did not think about how to configure custom group backends yet! $backendTypes = array( 'db' => $this->translate('Database'), - 'ldap' => $this->translate('LDAP'), - 'msldap' => $this->translate('ActiveDirectory') + 'ldap' => 'LDAP', + 'msldap' => 'ActiveDirectory' ); $backendType = isset($formData['type']) ? $formData['type'] : null; From 76e748c483fb79efc134ef6b492b60131fec56c4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 17:12:32 +0200 Subject: [PATCH 39/54] Don't throw any exception refs #8705 --- library/Icinga/Application/Platform.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Application/Platform.php b/library/Icinga/Application/Platform.php index a6e0d9604..cc72857f9 100644 --- a/library/Icinga/Application/Platform.php +++ b/library/Icinga/Application/Platform.php @@ -3,8 +3,6 @@ namespace Icinga\Application; -use Icinga\Exception\IcingaException; - /** * Platform tests for icingaweb */ @@ -95,7 +93,7 @@ class Platform foreach ($osRelease as $osInfo) { if (false === ($res = @preg_match('/(? Date: Fri, 5 Jun 2015 17:20:31 +0200 Subject: [PATCH 40/54] UserBackendConfigForm: Allow to configure user backends of type msldap fixes #9355 --- .../Config/UserBackend/LdapBackendForm.php | 33 ++++++++++++------- .../forms/Config/UserBackendConfigForm.php | 29 ++++++++++------ .../UserBackend/LdapBackendFormTest.php | 23 ++++++------- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/application/forms/Config/UserBackend/LdapBackendForm.php b/application/forms/Config/UserBackend/LdapBackendForm.php index c520ec68f..ac2091398 100644 --- a/application/forms/Config/UserBackend/LdapBackendForm.php +++ b/application/forms/Config/UserBackend/LdapBackendForm.php @@ -8,7 +8,7 @@ use Icinga\Web\Form; use Icinga\Data\ConfigObject; use Icinga\Data\ResourceFactory; use Icinga\Exception\AuthenticationException; -use Icinga\Authentication\User\LdapUserBackend; +use Icinga\Authentication\User\UserBackend; /** * Form class for adding/modifying LDAP user backends @@ -48,6 +48,8 @@ class LdapBackendForm extends Form */ public function createElements(array $formData) { + $isAd = isset($formData['type']) ? $formData['type'] === 'msldap' : false; + $this->addElement( 'text', 'name', @@ -77,10 +79,13 @@ class LdapBackendForm extends Form 'text', 'user_class', array( - 'required' => true, - 'label' => $this->translate('LDAP User Object Class'), - 'description' => $this->translate('The object class used for storing users on the LDAP server.'), - 'value' => 'inetOrgPerson' + 'preserveDefault' => true, + 'required' => ! $isAd, + 'ignore' => $isAd, + 'disabled' => $isAd ?: null, + 'label' => $this->translate('LDAP User Object Class'), + 'description' => $this->translate('The object class used for storing users on the LDAP server.'), + 'value' => $isAd ? 'user' : 'inetOrgPerson' ) ); $this->addElement( @@ -117,12 +122,15 @@ class LdapBackendForm extends Form 'text', 'user_name_attribute', array( - 'required' => true, - 'label' => $this->translate('LDAP User Name Attribute'), - 'description' => $this->translate( + 'preserveDefault' => true, + 'required' => ! $isAd, + 'ignore' => $isAd, + 'disabled' => $isAd ?: null, + 'label' => $this->translate('LDAP User Name Attribute'), + 'description' => $this->translate( 'The attribute name used for storing the user name on the LDAP server.' ), - 'value' => 'uid' + 'value' => $isAd ? 'sAMAccountName' : 'uid' ) ); $this->addElement( @@ -130,7 +138,7 @@ class LdapBackendForm extends Form 'backend', array( 'disabled' => true, - 'value' => 'ldap' + 'value' => $isAd ? 'msldap' : 'ldap' ) ); $this->addElement( @@ -170,8 +178,7 @@ class LdapBackendForm extends Form public static function isValidUserBackend(Form $form) { try { - $ldapUserBackend = new LdapUserBackend(ResourceFactory::createResource($form->getResourceConfig())); - $ldapUserBackend->setConfig(new ConfigObject($form->getValues())); + $ldapUserBackend = UserBackend::create(null, new ConfigObject($form->getValues())); $ldapUserBackend->assertAuthenticationPossible(); } catch (AuthenticationException $e) { if (($previous = $e->getPrevious()) !== null) { @@ -193,6 +200,8 @@ class LdapBackendForm extends Form * Return the configuration for the chosen resource * * @return ConfigObject + * + * @todo Check whether it's possible to drop this (Or even all occurences!) */ public function getResourceConfig() { diff --git a/application/forms/Config/UserBackendConfigForm.php b/application/forms/Config/UserBackendConfigForm.php index 0a30dd590..62a68e70b 100644 --- a/application/forms/Config/UserBackendConfigForm.php +++ b/application/forms/Config/UserBackendConfigForm.php @@ -60,16 +60,24 @@ class UserBackendConfigForm extends ConfigForm */ public function getBackendForm($type) { - if ($type === 'db') { - $form = new DbBackendForm(); - $form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array()); - } elseif ($type === 'ldap') { - $form = new LdapBackendForm(); - $form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array()); - } elseif ($type === 'external') { - $form = new ExternalBackendForm(); - } else { - throw new InvalidArgumentException(sprintf($this->translate('Invalid backend type "%s" provided'), $type)); + switch ($type) + { + case 'db': + $form = new DbBackendForm(); + $form->setResources(isset($this->resources['db']) ? $this->resources['db'] : array()); + break; + case 'ldap': + case 'msldap': + $form = new LdapBackendForm(); + $form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array()); + break; + case 'external': + $form = new ExternalBackendForm(); + break; + default: + throw new InvalidArgumentException( + sprintf($this->translate('Invalid backend type "%s" provided'), $type) + ); } return $form; @@ -296,6 +304,7 @@ class UserBackendConfigForm extends ConfigForm } if (isset($this->resources['ldap']) && ($backendType === 'ldap' || Platform::extensionLoaded('ldap'))) { $backendTypes['ldap'] = 'LDAP'; + $backendTypes['msldap'] = 'ActiveDirectory'; } $externalBackends = array_filter( diff --git a/test/php/application/forms/Config/UserBackend/LdapBackendFormTest.php b/test/php/application/forms/Config/UserBackend/LdapBackendFormTest.php index f7373a7ae..6fe63adf4 100644 --- a/test/php/application/forms/Config/UserBackend/LdapBackendFormTest.php +++ b/test/php/application/forms/Config/UserBackend/LdapBackendFormTest.php @@ -27,10 +27,9 @@ class LdapBackendFormTest extends BaseTestCase */ public function testValidBackendIsValid() { - $this->setUpResourceFactoryMock(); - Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend') - ->shouldReceive('assertAuthenticationPossible')->andReturnNull() - ->shouldReceive('setConfig')->andReturnNull(); + $ldapUserBackendMock = Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend'); + $ldapUserBackendMock->shouldReceive('assertAuthenticationPossible')->andReturnNull(); + $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)); @@ -53,9 +52,9 @@ class LdapBackendFormTest extends BaseTestCase */ public function testInvalidBackendIsNotValid() { - $this->setUpResourceFactoryMock(); - Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend') - ->shouldReceive('assertAuthenticationPossible')->andThrow(new AuthenticationException); + $ldapUserBackendMock = Mockery::mock('overload:Icinga\Authentication\User\LdapUserBackend'); + $ldapUserBackendMock->shouldReceive('assertAuthenticationPossible')->andThrow(new AuthenticationException); + $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)); @@ -72,12 +71,10 @@ class LdapBackendFormTest extends BaseTestCase ); } - protected function setUpResourceFactoryMock() + protected function setUpUserBackendMock($ldapUserBackendMock) { - Mockery::mock('alias:Icinga\Data\ResourceFactory') - ->shouldReceive('createResource') - ->andReturn(Mockery::mock('Icinga\Protocol\Ldap\Connection')) - ->shouldReceive('getResourceConfig') - ->andReturn(new ConfigObject()); + Mockery::mock('alias:Icinga\Authentication\User\UserBackend') + ->shouldReceive('create') + ->andReturn($ldapUserBackendMock); } } From 2dd04bb6c96cabce1a8a9ed7e72f627ef6b582bd Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 5 Jun 2015 17:24:03 +0200 Subject: [PATCH 41/54] LdapUserGroupBackendForm: Do not disable `user_filter' and `group_filter' refs #7343 --- .../forms/Config/UserGroup/LdapUserGroupBackendForm.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 1ce6af713..c3c8baacf 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -118,8 +118,6 @@ class LdapUserGroupBackendForm extends Form array( 'preserveDefault' => true, 'allowEmpty' => true, - 'ignore' => $disabled, - 'disabled' => $disabled, 'label' => $this->translate('LDAP Group Filter'), 'description' => $this->translate( 'An additional filter to use when looking up groups using the specified connection. ' @@ -202,8 +200,8 @@ class LdapUserGroupBackendForm extends Form array( 'preserveDefault' => true, 'allowEmpty' => true, - 'ignore' => $disabled, - 'disabled' => $disabled, + 'ignore' => $dnDisabled, + 'disabled' => $dnDisabled, 'label' => $this->translate('LDAP User Filter'), 'description' => $this->translate( 'An additional filter to use when looking up users using the specified connection. ' From e15b144155dc9c989eefa57749409e2883f58b49 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 8 Jun 2015 11:16:59 +0200 Subject: [PATCH 42/54] Revert "Rename .fileNotReadable to .message-error" This reverts commit 6b14195809ce51bdf6f1c803af165438e948bd72. --- public/css/icinga/layout-structure.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 5b5eee5ef..3a8713a38 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -331,7 +331,8 @@ html { position: absolute; } -.message-error { +/* TODO: replace this with .error */ +.fileNotReadable { padding: 0.5em; background-color: @colorCritical; font-weight: bold; From 0b1bb54c26f5115367cf7e5655ec63bd6d74b1d5 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 5 Jun 2015 12:38:55 +0200 Subject: [PATCH 43/54] Rename .fileNotReadable to .message-error refs #9247 --- application/views/scripts/about/index.phtml | 2 +- public/css/icinga/layout-structure.less | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml index 6a01e1bf9..e270a8eb9 100644 --- a/application/views/scripts/about/index.phtml +++ b/application/views/scripts/about/index.phtml @@ -16,7 +16,7 @@ echo ( 0 === count($versionInfo) - ? '

    ' . $this->translate( + ? '

    ' . $this->translate( 'Can\'t determine Icinga Web 2\'s version' ) : '

    ' . nl2br(implode("\n", $versionInfo), false) diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 3a8713a38..5b5eee5ef 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -331,8 +331,7 @@ html { position: absolute; } -/* TODO: replace this with .error */ -.fileNotReadable { +.message-error { padding: 0.5em; background-color: @colorCritical; font-weight: bold; From 54b960addca31bba33684b552a575047534a63da Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 8 Jun 2015 12:39:31 +0200 Subject: [PATCH 44/54] RoleController: Fix link to add a new role --- application/views/scripts/role/list.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/scripts/role/list.phtml b/application/views/scripts/role/list.phtml index 766ba26f3..568bbdf98 100644 --- a/application/views/scripts/role/list.phtml +++ b/application/views/scripts/role/list.phtml @@ -67,7 +67,7 @@ - + translate('Create a New Role') ?>

    From ddba1d86fa0d82eae257067bfac78ccf371dd80b Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Mon, 8 Jun 2015 12:22:27 +0200 Subject: [PATCH 45/54] Fix spacing in ini files refs #8706 --- library/Icinga/File/Ini/IniEditor.php | 25 ++++++++++++------------- library/Icinga/File/Ini/IniWriter.php | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/library/Icinga/File/Ini/IniEditor.php b/library/Icinga/File/Ini/IniEditor.php index 1f8202d9d..035e10037 100644 --- a/library/Icinga/File/Ini/IniEditor.php +++ b/library/Icinga/File/Ini/IniEditor.php @@ -313,14 +313,19 @@ class IniEditor */ public function getText() { - $this->cleanUpWhitespaces(); - return rtrim(implode(PHP_EOL, $this->text)) . PHP_EOL; + $this->normalizeSectionSpacing(); + + // trim leading and trailing whitespaces from generated file + $txt = trim(implode(PHP_EOL, $this->text)) . PHP_EOL; + + // replace linebreaks, unless they preceed a comment or a section + return preg_replace("/\n[\n]*([^;\[])/", "\n$1", $txt); } /** - * Remove all unneeded line breaks between sections + * normalize section spacing according to the current settings */ - private function cleanUpWhitespaces() + private function normalizeSectionSpacing() { $i = count($this->text) - 1; for (; $i > 0; $i--) { @@ -328,24 +333,18 @@ class IniEditor if ($this->isSectionDeclaration($line) && $i > 0) { $i--; $line = $this->text[$i]; - /* - * Ignore comments that are glued to the section declaration - */ + // ignore comments that are glued to the section declaration while ($i > 0 && $this->isComment($line)) { $i--; $line = $this->text[$i]; } - /* - * Remove whitespaces between the sections - */ + // remove whitespaces between the sections while ($i > 0 && preg_match('/^\s*$/', $line) === 1) { $this->deleteLine($i); $i--; $line = $this->text[$i]; } - /* - * Refresh section separators - */ + // refresh section separators if ($i !== 0 && $this->sectionSeparators > 0) { $this->insertAtLine($i + 1, str_repeat(PHP_EOL, $this->sectionSeparators - 1)); } diff --git a/library/Icinga/File/Ini/IniWriter.php b/library/Icinga/File/Ini/IniWriter.php index e43c226aa..5b565a6a0 100644 --- a/library/Icinga/File/Ini/IniWriter.php +++ b/library/Icinga/File/Ini/IniWriter.php @@ -60,7 +60,7 @@ class IniWriter extends Zend_Config_Writer_FileAbstract { if (file_exists($this->_filename)) { $oldconfig = new Zend_Config_Ini($this->_filename); - $content = file_get_contents($this->_filename); + $content = trim(file_get_contents($this->_filename)); } else { $oldconfig = new Zend_Config(array()); $content = ''; From 3600e1088c2c9d6d7e81a5aa3d4e114d0b0529cb Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Mon, 8 Jun 2015 13:10:21 +0200 Subject: [PATCH 46/54] Fix key and value sanitazion in ini writer --- library/Icinga/File/Ini/IniEditor.php | 2 +- .../library/Icinga/File/Ini/IniWriterTest.php | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/library/Icinga/File/Ini/IniEditor.php b/library/Icinga/File/Ini/IniEditor.php index 035e10037..00bea8df4 100644 --- a/library/Icinga/File/Ini/IniEditor.php +++ b/library/Icinga/File/Ini/IniEditor.php @@ -620,6 +620,6 @@ class IniEditor private function sanitize($value) { - return str_replace('\n', '', $value); + return str_replace("\n", '', $value); } } diff --git a/test/php/library/Icinga/File/Ini/IniWriterTest.php b/test/php/library/Icinga/File/Ini/IniWriterTest.php index 2d9659675..fa9ad895f 100644 --- a/test/php/library/Icinga/File/Ini/IniWriterTest.php +++ b/test/php/library/Icinga/File/Ini/IniWriterTest.php @@ -719,6 +719,34 @@ EOD; ); } + public function testWhetherLinebreaksAreRemoved() + { + $target = $this->writeConfigToTemporaryFile(''); + $writer = new IniWriter( + array( + 'config' => Config::fromArray( + array( + 'section' => array( + 'foo' => 'linebreak +in line', + 'linebreak +inkey' => 'blarg' + ) + ) + ), + 'filename' => $target + ) + ); + + $rendered = $writer->render(); + var_dump($rendered); + $this->assertEquals( + count(explode("\n", $rendered)), + 4, + 'generated config should not contain more than three line breaks' + ); + } + /** * Write a INI-configuration string to a temporary file and return its path * From 07041fafc8f617254dc7243da5002db1563d5698 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 8 Jun 2015 13:27:45 +0200 Subject: [PATCH 47/54] UserController: Behave nicely when not any backend is available --- application/controllers/UserController.php | 4 ++++ application/views/scripts/user/list.phtml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/application/controllers/UserController.php b/application/controllers/UserController.php index e944af52c..5abdaece8 100644 --- a/application/controllers/UserController.php +++ b/application/controllers/UserController.php @@ -28,6 +28,10 @@ class UserController extends AuthBackendController function ($b) { return $b->getName(); }, $this->loadUserBackends('Icinga\Data\Selectable') ); + if (empty($backendNames)) { + return; + } + $this->view->backendSelection = new Form(); $this->view->backendSelection->setAttrib('class', 'backend-selection'); $this->view->backendSelection->setUidDisabled(); diff --git a/application/views/scripts/user/list.phtml b/application/views/scripts/user/list.phtml index 76a6f2b8b..df9a14a45 100644 --- a/application/views/scripts/user/list.phtml +++ b/application/views/scripts/user/list.phtml @@ -18,7 +18,7 @@ if (! $this->compact): ?>
    translate('No backend found which is able to list users') . '
    '; return; } else { From 91cee3a957cf0d272aa9cfcaad788c36e9828b3a Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 8 Jun 2015 13:28:12 +0200 Subject: [PATCH 48/54] GroupController: Behave nicely when not any backend is available --- application/controllers/GroupController.php | 4 ++++ application/views/scripts/group/list.phtml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/application/controllers/GroupController.php b/application/controllers/GroupController.php index 8f2c33e62..395b2f243 100644 --- a/application/controllers/GroupController.php +++ b/application/controllers/GroupController.php @@ -28,6 +28,10 @@ class GroupController extends AuthBackendController function ($b) { return $b->getName(); }, $this->loadUserGroupBackends('Icinga\Data\Selectable') ); + if (empty($backendNames)) { + return; + } + $this->view->backendSelection = new Form(); $this->view->backendSelection->setAttrib('class', 'backend-selection'); $this->view->backendSelection->setUidDisabled(); diff --git a/application/views/scripts/group/list.phtml b/application/views/scripts/group/list.phtml index bcd9dca93..65c1591cd 100644 --- a/application/views/scripts/group/list.phtml +++ b/application/views/scripts/group/list.phtml @@ -18,7 +18,7 @@ if (! $this->compact): ?>
    translate('No backend found which is able to list groups') . '
    '; return; } else { From 44e3dba36226451afb2a3f0954ae21bc3bb6c7ba Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Mon, 8 Jun 2015 16:19:03 +0200 Subject: [PATCH 49/54] Improve group and user view layout Display header and hide filter and sort widgets when there are no entries to be sorted or filtered. --- application/views/scripts/group/show.phtml | 11 ++++++----- application/views/scripts/user/show.phtml | 11 ++++++----- public/css/icinga/main-content.less | 10 ++++++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/application/views/scripts/group/show.phtml b/application/views/scripts/group/show.phtml index 636a449d4..8c8c91f51 100644 --- a/application/views/scripts/group/show.phtml +++ b/application/views/scripts/group/show.phtml @@ -30,8 +30,12 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan

    escape($group->group_name); ?>

    translate('Created at'); ?>: created_at === null ? '-' : $this->formatDateTime($group->created_at); ?>

    -

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?>

    +

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?>

    +
    translate('Members'); ?>
    +

    +
    + 0): ?> compact): ?> sortBox; ?> @@ -40,9 +44,6 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan compact): ?> filterEditor; ?> -
    -
    - 0): ?> @@ -66,7 +67,7 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan
    -

    translate('No group member found matching the filter'); ?>

    +

    translate('Group has no members.'); ?>

    qlink($this->translate('Add a new member'), 'group/addmember', array( diff --git a/application/views/scripts/user/show.phtml b/application/views/scripts/user/show.phtml index 82f4c53f9..f4e9dee6a 100644 --- a/application/views/scripts/user/show.phtml +++ b/application/views/scripts/user/show.phtml @@ -30,8 +30,12 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc

    escape($user->user_name); ?>

    translate('State'); ?>: is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?>

    translate('Created at'); ?>: created_at === null ? '-' : $this->formatDateTime($user->created_at); ?>

    -

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?>

    +

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?>

    +
    translate('Group Memberships'); ?>
    + +
    + 0): ?> compact): ?> sortBox; ?> @@ -40,9 +44,6 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc compact): ?> filterEditor; ?> -
    -
    - 0): ?> @@ -80,7 +81,7 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc
    -

    translate('No memberships found matching the filter'); ?>

    + translate('User has no group memberships.'); ?> qlink($this->translate('Create new membership'), 'user/createmembership', array( diff --git a/public/css/icinga/main-content.less b/public/css/icinga/main-content.less index 18bc03a57..06269d271 100644 --- a/public/css/icinga/main-content.less +++ b/public/css/icinga/main-content.less @@ -236,8 +236,9 @@ div.content.users { } div.controls div.user-header { - border-bottom: 2px solid @colorPetrol; - margin-bottom: 1em; + h5 { + margin-bottom: 0em; + } .user-name { display: inline-block; @@ -302,8 +303,9 @@ div.content.groups { } div.controls div.group-header { - border-bottom: 2px solid @colorPetrol; - margin-bottom: 1em; + h5 { + margin-bottom: 0em; + } .group-name { display: inline-block; From 17fa0d3f11d4f494e6456f5e5b2c6914d00deef6 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 9 Jun 2015 08:23:06 +0200 Subject: [PATCH 50/54] Revert "Improve group and user view layout" This reverts commit 44e3dba36226451afb2a3f0954ae21bc3bb6c7ba. Reason: * Controls are controls, not content * Controls must still be shown in case of a non-matching filter --- application/views/scripts/group/show.phtml | 11 +++++------ application/views/scripts/user/show.phtml | 11 +++++------ public/css/icinga/main-content.less | 10 ++++------ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/application/views/scripts/group/show.phtml b/application/views/scripts/group/show.phtml index 8c8c91f51..636a449d4 100644 --- a/application/views/scripts/group/show.phtml +++ b/application/views/scripts/group/show.phtml @@ -30,12 +30,8 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan

    escape($group->group_name); ?>

    translate('Created at'); ?>: created_at === null ? '-' : $this->formatDateTime($group->created_at); ?>

    -

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?>

    -
    translate('Members'); ?>
    +

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?>

    -
    -
    - 0): ?> compact): ?> sortBox; ?> @@ -44,6 +40,9 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan compact): ?> filterEditor; ?> +
    +
    + 0): ?> @@ -67,7 +66,7 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan
    -

    translate('Group has no members.'); ?>

    +

    translate('No group member found matching the filter'); ?>

    qlink($this->translate('Add a new member'), 'group/addmember', array( diff --git a/application/views/scripts/user/show.phtml b/application/views/scripts/user/show.phtml index f4e9dee6a..82f4c53f9 100644 --- a/application/views/scripts/user/show.phtml +++ b/application/views/scripts/user/show.phtml @@ -30,12 +30,8 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc

    escape($user->user_name); ?>

    translate('State'); ?>: is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?>

    translate('Created at'); ?>: created_at === null ? '-' : $this->formatDateTime($user->created_at); ?>

    -

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?>

    -
    translate('Group Memberships'); ?>
    +

    translate('Last modified'); ?>: last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?>

    - -
    - 0): ?> compact): ?> sortBox; ?> @@ -44,6 +40,9 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc compact): ?> filterEditor; ?> +
    +
    + 0): ?> @@ -81,7 +80,7 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc
    - translate('User has no group memberships.'); ?> +

    translate('No memberships found matching the filter'); ?>

    qlink($this->translate('Create new membership'), 'user/createmembership', array( diff --git a/public/css/icinga/main-content.less b/public/css/icinga/main-content.less index 06269d271..18bc03a57 100644 --- a/public/css/icinga/main-content.less +++ b/public/css/icinga/main-content.less @@ -236,9 +236,8 @@ div.content.users { } div.controls div.user-header { - h5 { - margin-bottom: 0em; - } + border-bottom: 2px solid @colorPetrol; + margin-bottom: 1em; .user-name { display: inline-block; @@ -303,9 +302,8 @@ div.content.groups { } div.controls div.group-header { - h5 { - margin-bottom: 0em; - } + border-bottom: 2px solid @colorPetrol; + margin-bottom: 1em; .group-name { display: inline-block; From 27d2af56166c0dfdd55fdd7b683d1ad791e01b5c Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 9 Jun 2015 08:31:05 +0200 Subject: [PATCH 51/54] UserController: Allow to quick search memberships --- application/controllers/UserController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/controllers/UserController.php b/application/controllers/UserController.php index 5abdaece8..54482dd7d 100644 --- a/application/controllers/UserController.php +++ b/application/controllers/UserController.php @@ -103,6 +103,7 @@ class UserController extends AuthBackendController $filterEditor = Widget::create('filterEditor') ->setQuery($memberships) + ->setSearchColumns(array('group_name')) ->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'user') ->ignoreParams('page') ->handleRequest($this->getRequest()); From fb52ee5a7c1948fe6eb32395edc4ce5c2669d521 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 9 Jun 2015 08:31:21 +0200 Subject: [PATCH 52/54] GroupController: Allow to quick search members --- application/controllers/GroupController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/controllers/GroupController.php b/application/controllers/GroupController.php index 395b2f243..a4161981a 100644 --- a/application/controllers/GroupController.php +++ b/application/controllers/GroupController.php @@ -104,6 +104,7 @@ class GroupController extends AuthBackendController $filterEditor = Widget::create('filterEditor') ->setQuery($members) + ->setSearchColumns(array('user')) ->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'group') ->ignoreParams('page') ->handleRequest($this->getRequest()); From f2c7d5686a796c8fc3a08e5299d14da866deff40 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 9 Jun 2015 09:17:15 +0200 Subject: [PATCH 53/54] Timeline: Fix url used to extend the timeline --- .../monitoring/application/views/scripts/timeline/index.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/timeline/index.phtml b/modules/monitoring/application/views/scripts/timeline/index.phtml index d0cc8d34c..209eb0f1b 100644 --- a/modules/monitoring/application/views/scripts/timeline/index.phtml +++ b/modules/monitoring/application/views/scripts/timeline/index.phtml @@ -132,8 +132,8 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g