From 7960e911a661984a732ae8364ffe0508c89d29ca Mon Sep 17 00:00:00 2001 From: Johannes Meyer <johannes.meyer@netways.de> Date: Wed, 22 Apr 2015 09:52:08 +0200 Subject: [PATCH] UserGroupBackend: Add support for custom backends to fetch user groups refs #8826 refs #9122 --- library/Icinga/Application/Modules/Module.php | 32 +++++ .../UserGroup/UserGroupBackend.php | 110 ++++++++++++++++-- 2 files changed, 135 insertions(+), 7 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index a575b9b9a..516e6ad0f 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -192,6 +192,13 @@ class Module */ protected $userBackends = array(); + /** + * This module's user group backends + * + * @var array + */ + protected $userGroupBackends = array(); + /** * Provide a search URL * @@ -727,6 +734,17 @@ class Module return $this->userBackends; } + /** + * Return this module's user group backends + * + * @return array + */ + public function getUserGroupBackends() + { + $this->launchConfigScript(); + return $this->userGroupBackends; + } + /** * Provide a named permission * @@ -816,6 +834,20 @@ class Module return $this; } + /** + * Provide a user group backend + * + * @param string $identifier The identifier of the new backend type + * @param string $className The name of the class + * + * @return $this + */ + protected function provideUserGroupBackend($identifier, $className) + { + $this->userGroupBackends[strtolower($identifier)] = $className; + return $this; + } + /** * Register new namespaces on the autoloader * diff --git a/library/Icinga/Authentication/UserGroup/UserGroupBackend.php b/library/Icinga/Authentication/UserGroup/UserGroupBackend.php index 1cc7abec5..410b5114d 100644 --- a/library/Icinga/Authentication/UserGroup/UserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/UserGroupBackend.php @@ -3,6 +3,8 @@ namespace Icinga\Authentication\UserGroup; +use Icinga\Application\Logger; +use Icinga\Application\Icinga; use Icinga\Data\ConfigObject; use Icinga\Data\ResourceFactory; use Icinga\Exception\ConfigurationError; @@ -13,6 +15,23 @@ use Icinga\User; */ abstract class UserGroupBackend { + /** + * The default user group backend types provided by Icinga Web 2 + * + * @var array + */ + private static $defaultBackends = array( // I would have liked it if I were able to declare this as constant :'( + 'db', + 'ini' + ); + + /** + * The registered custom user group backends with their identifier as key and class name as value + * + * @var array + */ + protected static $customBackends; + /** * The name of this backend * @@ -43,6 +62,75 @@ abstract class UserGroupBackend return $this->name; } + /** + * Fetch all custom user group backends from all loaded modules + */ + public static function loadCustomUserGroupBackends() + { + if (static::$customBackends !== null) { + return; + } + + static::$customBackends = array(); + $providedBy = array(); + foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) { + foreach ($module->getUserGroupBackends() as $identifier => $className) { + if (array_key_exists($identifier, $providedBy)) { + Logger::warning( + 'Cannot register UserGroupBackend of type "%s" provided by module "%s".' + . ' The type is already provided by module "%s"', + $identifier, + $module->getName(), + $providedBy[$identifier] + ); + } elseif (in_array($identifier, static::$defaultBackends)) { + Logger::warning( + 'Cannot register UserGroupBackend of type "%s" provided by module "%s".' + . ' The type is a default type provided by Icinga Web 2', + $identifier, + $module->getName() + ); + } else { + $providedBy[$identifier] = $module->getName(); + static::$customBackends[$identifier] = $className; + } + } + } + } + + /** + * Validate and return the class for the given custom user group backend + * + * @param string $identifier The identifier of the custom user group backend + * + * @return string|null The name of the class or null in case there was no + * backend found with the given identifier + * + * @throws ConfigurationError In case the class could not be successfully validated + */ + protected static function getCustomUserGroupBackend($identifier) + { + static::loadCustomUserGroupBackends(); + if (array_key_exists($identifier, static::$customBackends)) { + $className = static::$customBackends[$identifier]; + if (! class_exists($className)) { + throw new ConfigurationError( + 'Cannot utilize UserGroupBackend of type "%s". Class "%s" does not exist', + $identifier, + $className + ); + } elseif (! is_subclass_of($className, __CLASS__)) { + throw new ConfigurationError( + 'Cannot utilize UserGroupBackend of type "%s". Class "%s" is not a sub-type of UserGroupBackend', + $identifier, + $className + ); + } + + return $className; + } + } + /** * Create and return a UserGroupBackend with the given name and given configuration applied to it * @@ -65,6 +153,21 @@ abstract class UserGroupBackend $name ); } + if (in_array($backendType, static::$defaultBackends)) { + // The default backend check is the first one because of performance reasons: + // Do not attempt to load a custom user group backend unless it's actually required + } elseif (($customClass = static::getCustomUserGroupBackend($backendType)) !== null) { + $backend = new $customClass($backendConfig); + $backend->setName($name); + return $backend; + } else { + throw new ConfigurationError( + 'Configuration for user group backend "%s" defines an invalid backend type.' + . ' Backend type "%s" is not supported', + $name, + $backendType + ); + } if ($backendConfig->resource === null) { throw new ConfigurationError( @@ -81,13 +184,6 @@ abstract class UserGroupBackend case 'ini': $backend = new IniUserGroupBackend($resource); break; - default: - throw new ConfigurationError( - 'Configuration for user group backend "%s" defines an invalid backend type.' - . ' Backend type "%s" is not supported', - $name, - $backendType - ); } $backend->setName($name);