From 299128ce43d71c28077fd6575ba06b53c0dd6cb2 Mon Sep 17 00:00:00 2001 From: Allon Moritz Date: Fri, 13 Jan 2023 16:34:01 +0100 Subject: [PATCH 1/4] Converts the authentication plugins to service providers --- plugins/authentication/cookie/cookie.xml | 4 +- .../cookie/services/provider.php | 49 ++++++ .../{cookie.php => src/Extension/Cookie.php} | 148 ++++++++---------- plugins/authentication/joomla/joomla.xml | 4 +- .../joomla/services/provider.php | 49 ++++++ .../{joomla.php => src/Extension/Joomla.php} | 36 ++--- plugins/authentication/ldap/ldap.xml | 4 +- .../authentication/ldap/services/provider.php | 49 ++++++ .../ldap/{ldap.php => src/Extension/Ldap.php} | 39 ++--- 9 files changed, 254 insertions(+), 128 deletions(-) create mode 100644 plugins/authentication/cookie/services/provider.php rename plugins/authentication/cookie/{cookie.php => src/Extension/Cookie.php} (65%) create mode 100644 plugins/authentication/joomla/services/provider.php rename plugins/authentication/joomla/{joomla.php => src/Extension/Joomla.php} (77%) create mode 100644 plugins/authentication/ldap/services/provider.php rename plugins/authentication/ldap/{ldap.php => src/Extension/Ldap.php} (85%) diff --git a/plugins/authentication/cookie/cookie.xml b/plugins/authentication/cookie/cookie.xml index 5a4a2084236f1..50963a6b5c808 100644 --- a/plugins/authentication/cookie/cookie.xml +++ b/plugins/authentication/cookie/cookie.xml @@ -9,8 +9,10 @@ www.joomla.org 3.0.0 PLG_AUTHENTICATION_COOKIE_XML_DESCRIPTION + Joomla\Plugin\Authentication\Cookie - cookie.php + services + src language/en-GB/plg_authentication_cookie.ini diff --git a/plugins/authentication/cookie/services/provider.php b/plugins/authentication/cookie/services/provider.php new file mode 100644 index 0000000000000..a77f1d4611e77 --- /dev/null +++ b/plugins/authentication/cookie/services/provider.php @@ -0,0 +1,49 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Database\DatabaseInterface; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Authentication\Cookie\Extension\Cookie; + +return new class implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register(Container $container) + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new Cookie( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('authentication', 'cookie') + ); + $plugin->setApplication(Factory::getApplication()); + $plugin->setDatabase($container->get(DatabaseInterface::class)); + + return $plugin; + } + ); + } +}; diff --git a/plugins/authentication/cookie/cookie.php b/plugins/authentication/cookie/src/Extension/Cookie.php similarity index 65% rename from plugins/authentication/cookie/cookie.php rename to plugins/authentication/cookie/src/Extension/Cookie.php index 48d9c83ffcac7..cab2a0b88cff9 100644 --- a/plugins/authentication/cookie/cookie.php +++ b/plugins/authentication/cookie/src/Extension/Cookie.php @@ -6,10 +6,10 @@ * * @copyright (C) 2013 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt - - * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ +namespace Joomla\Plugin\Authentication\Cookie\Extension; + use Joomla\CMS\Authentication\Authentication; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; @@ -17,6 +17,8 @@ use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; +use Joomla\Database\DatabaseAwareTrait; +use RuntimeException; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -29,23 +31,9 @@ * @note Code based on http://jaspan.com/improved_persistent_login_cookie_best_practice * and http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/ */ -class PlgAuthenticationCookie extends CMSPlugin +final class Cookie extends CMSPlugin { - /** - * Application object - * - * @var \Joomla\CMS\Application\CMSApplication - * @since 3.2 - */ - protected $app; - - /** - * Database object - * - * @var \Joomla\Database\DatabaseDriver - * @since 3.2 - */ - protected $db; + use DatabaseAwareTrait; /** * Reports the privacy related capabilities for this plugin to site administrators. @@ -59,8 +47,8 @@ public function onPrivacyCollectAdminCapabilities() $this->loadLanguage(); return array( - Text::_('PLG_AUTHENTICATION_COOKIE') => array( - Text::_('PLG_AUTHENTICATION_COOKIE_PRIVACY_CAPABILITY_COOKIE'), + $this->getApplication()->getLanguage()->_('PLG_AUTHENTICATION_COOKIE') => array( + $this->getApplication()->getLanguage()->_('PLG_AUTHENTICATION_COOKIE_PRIVACY_CAPABILITY_COOKIE'), ), ); } @@ -79,18 +67,18 @@ public function onPrivacyCollectAdminCapabilities() public function onUserAuthenticate($credentials, $options, &$response) { // No remember me for admin - if ($this->app->isClient('administrator')) { + if ($this->getApplication()->isClient('administrator')) { return false; } // Get cookie $cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent(); - $cookieValue = $this->app->getInput()->cookie->get($cookieName); + $cookieValue = $this->getApplication()->getInput()->cookie->get($cookieName); // Try with old cookieName (pre 3.6.0) if not found if (!$cookieValue) { $cookieName = UserHelper::getShortHashedUserAgent(); - $cookieValue = $this->app->getInput()->cookie->get($cookieName); + $cookieValue = $this->getApplication()->getInput()->cookie->get($cookieName); } if (!$cookieValue) { @@ -102,7 +90,7 @@ public function onUserAuthenticate($credentials, $options, &$response) // Check for valid cookie value if (count($cookieArray) !== 2) { // Destroy the cookie in the browser. - $this->app->getInput()->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); + $this->getApplication()->getInput()->cookie->set($cookieName, '', 1, $this->getApplication()->get('cookie_path', '/'), $this->getApplication()->get('cookie_domain', '')); Log::add('Invalid cookie detected.', Log::WARNING, 'error'); return false; @@ -116,29 +104,29 @@ public function onUserAuthenticate($credentials, $options, &$response) $now = time(); // Remove expired tokens - $query = $this->db->getQuery(true) - ->delete($this->db->quoteName('#__user_keys')) - ->where($this->db->quoteName('time') . ' < :now') + $query = $this->getDatabase()->getQuery(true) + ->delete($this->getDatabase()->quoteName('#__user_keys')) + ->where($this->getDatabase()->quoteName('time') . ' < :now') ->bind(':now', $now); try { - $this->db->setQuery($query)->execute(); + $this->getDatabase()->setQuery($query)->execute(); } catch (RuntimeException $e) { // We aren't concerned with errors from this query, carry on } // Find the matching record if it exists. - $query = $this->db->getQuery(true) - ->select($this->db->quoteName(['user_id', 'token', 'series', 'time'])) - ->from($this->db->quoteName('#__user_keys')) - ->where($this->db->quoteName('series') . ' = :series') - ->where($this->db->quoteName('uastring') . ' = :uastring') - ->order($this->db->quoteName('time') . ' DESC') + $query = $this->getDatabase()->getQuery(true) + ->select($this->getDatabase()->quoteName(['user_id', 'token', 'series', 'time'])) + ->from($this->getDatabase()->quoteName('#__user_keys')) + ->where($this->getDatabase()->quoteName('series') . ' = :series') + ->where($this->getDatabase()->quoteName('uastring') . ' = :uastring') + ->order($this->getDatabase()->quoteName('time') . ' DESC') ->bind(':series', $series) ->bind(':uastring', $cookieName); try { - $results = $this->db->setQuery($query)->loadObjectList(); + $results = $this->getDatabase()->setQuery($query)->loadObjectList(); } catch (RuntimeException $e) { $response->status = Authentication::STATUS_FAILURE; @@ -147,7 +135,7 @@ public function onUserAuthenticate($credentials, $options, &$response) if (count($results) !== 1) { // Destroy the cookie in the browser. - $this->app->getInput()->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); + $this->getApplication()->getInput()->cookie->set($cookieName, '', 1, $this->getApplication()->get('cookie_path', '/'), $this->getApplication()->get('cookie_domain', '')); $response->status = Authentication::STATUS_FAILURE; return false; @@ -160,13 +148,13 @@ public function onUserAuthenticate($credentials, $options, &$response) * Either the series was guessed correctly or a cookie was stolen and used twice (once by attacker and once by victim). * Delete all tokens for this user! */ - $query = $this->db->getQuery(true) - ->delete($this->db->quoteName('#__user_keys')) - ->where($this->db->quoteName('user_id') . ' = :userid') + $query = $this->getDatabase()->getQuery(true) + ->delete($this->getDatabase()->quoteName('#__user_keys')) + ->where($this->getDatabase()->quoteName('user_id') . ' = :userid') ->bind(':userid', $results[0]->user_id); try { - $this->db->setQuery($query)->execute(); + $this->getDatabase()->setQuery($query)->execute(); } catch (RuntimeException $e) { // Log an alert for the site admin Log::add( @@ -177,7 +165,7 @@ public function onUserAuthenticate($credentials, $options, &$response) } // Destroy the cookie in the browser. - $this->app->getInput()->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); + $this->getApplication()->getInput()->cookie->set($cookieName, '', 1, $this->getApplication()->get('cookie_path', '/'), $this->getApplication()->get('cookie_domain', '')); // Issue warning by email to user and/or admin? Log::add(Text::sprintf('PLG_AUTHENTICATION_COOKIE_ERROR_LOG_LOGIN_FAILED', $results[0]->user_id), Log::WARNING, 'security'); @@ -187,15 +175,15 @@ public function onUserAuthenticate($credentials, $options, &$response) } // Make sure there really is a user with this name and get the data for the session. - $query = $this->db->getQuery(true) - ->select($this->db->quoteName(['id', 'username', 'password'])) - ->from($this->db->quoteName('#__users')) - ->where($this->db->quoteName('username') . ' = :userid') - ->where($this->db->quoteName('requireReset') . ' = 0') + $query = $this->getDatabase()->getQuery(true) + ->select($this->getDatabase()->quoteName(['id', 'username', 'password'])) + ->from($this->getDatabase()->quoteName('#__users')) + ->where($this->getDatabase()->quoteName('username') . ' = :userid') + ->where($this->getDatabase()->quoteName('requireReset') . ' = 0') ->bind(':userid', $results[0]->user_id); try { - $result = $this->db->setQuery($query)->loadObject(); + $result = $this->getDatabase()->setQuery($query)->loadObject(); } catch (RuntimeException $e) { $response->status = Authentication::STATUS_FAILURE; @@ -218,7 +206,7 @@ public function onUserAuthenticate($credentials, $options, &$response) $response->error_message = ''; } else { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_NO_USER'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER'); } } @@ -236,7 +224,7 @@ public function onUserAuthenticate($credentials, $options, &$response) public function onUserAfterLogin($options) { // No remember me for admin - if ($this->app->isClient('administrator')) { + if ($this->getApplication()->isClient('administrator')) { return false; } @@ -245,15 +233,15 @@ public function onUserAfterLogin($options) $cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent(); // We need the old data to get the existing series - $cookieValue = $this->app->getInput()->cookie->get($cookieName); + $cookieValue = $this->getApplication()->getInput()->cookie->get($cookieName); // Try with old cookieName (pre 3.6.0) if not found if (!$cookieValue) { $oldCookieName = UserHelper::getShortHashedUserAgent(); - $cookieValue = $this->app->getInput()->cookie->get($oldCookieName); + $cookieValue = $this->getApplication()->getInput()->cookie->get($oldCookieName); // Destroy the old cookie in the browser - $this->app->getInput()->cookie->set($oldCookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); + $this->getApplication()->getInput()->cookie->set($oldCookieName, '', 1, $this->getApplication()->get('cookie_path', '/'), $this->getApplication()->get('cookie_domain', '')); } $cookieArray = explode('.', $cookieValue); @@ -271,14 +259,14 @@ public function onUserAfterLogin($options) do { $series = UserHelper::genRandomPassword(20); - $query = $this->db->getQuery(true) - ->select($this->db->quoteName('series')) - ->from($this->db->quoteName('#__user_keys')) - ->where($this->db->quoteName('series') . ' = :series') + $query = $this->getDatabase()->getQuery(true) + ->select($this->getDatabase()->quoteName('series')) + ->from($this->getDatabase()->quoteName('#__user_keys')) + ->where($this->getDatabase()->quoteName('series') . ' = :series') ->bind(':series', $series); try { - $results = $this->db->setQuery($query)->loadResult(); + $results = $this->getDatabase()->setQuery($query)->loadResult(); if ($results === null) { $unique = true; @@ -305,28 +293,28 @@ public function onUserAfterLogin($options) $cookieValue = $token . '.' . $series; // Overwrite existing cookie with new value - $this->app->getInput()->cookie->set( + $this->getApplication()->getInput()->cookie->set( $cookieName, $cookieValue, time() + $lifetime, - $this->app->get('cookie_path', '/'), - $this->app->get('cookie_domain', ''), - $this->app->isHttpsForced(), + $this->getApplication()->get('cookie_path', '/'), + $this->getApplication()->get('cookie_domain', ''), + $this->getApplication()->isHttpsForced(), true ); - $query = $this->db->getQuery(true); + $query = $this->getDatabase()->getQuery(true); if (!empty($options['remember'])) { $future = (time() + $lifetime); // Create new record $query - ->insert($this->db->quoteName('#__user_keys')) - ->set($this->db->quoteName('user_id') . ' = :userid') - ->set($this->db->quoteName('series') . ' = :series') - ->set($this->db->quoteName('uastring') . ' = :uastring') - ->set($this->db->quoteName('time') . ' = :time') + ->insert($this->getDatabase()->quoteName('#__user_keys')) + ->set($this->getDatabase()->quoteName('user_id') . ' = :userid') + ->set($this->getDatabase()->quoteName('series') . ' = :series') + ->set($this->getDatabase()->quoteName('uastring') . ' = :uastring') + ->set($this->getDatabase()->quoteName('time') . ' = :time') ->bind(':userid', $options['user']->username) ->bind(':series', $series) ->bind(':uastring', $cookieName) @@ -334,10 +322,10 @@ public function onUserAfterLogin($options) } else { // Update existing record with new token $query - ->update($this->db->quoteName('#__user_keys')) - ->where($this->db->quoteName('user_id') . ' = :userid') - ->where($this->db->quoteName('series') . ' = :series') - ->where($this->db->quoteName('uastring') . ' = :uastring') + ->update($this->getDatabase()->quoteName('#__user_keys')) + ->where($this->getDatabase()->quoteName('user_id') . ' = :userid') + ->where($this->getDatabase()->quoteName('series') . ' = :series') + ->where($this->getDatabase()->quoteName('uastring') . ' = :uastring') ->bind(':userid', $options['user']->username) ->bind(':series', $series) ->bind(':uastring', $cookieName); @@ -345,11 +333,11 @@ public function onUserAfterLogin($options) $hashedToken = UserHelper::hashPassword($token); - $query->set($this->db->quoteName('token') . ' = :token') + $query->set($this->getDatabase()->quoteName('token') . ' = :token') ->bind(':token', $hashedToken); try { - $this->db->setQuery($query)->execute(); + $this->getDatabase()->setQuery($query)->execute(); } catch (RuntimeException $e) { return false; } @@ -369,12 +357,12 @@ public function onUserAfterLogin($options) public function onUserAfterLogout($options) { // No remember me for admin - if ($this->app->isClient('administrator')) { + if ($this->getApplication()->isClient('administrator')) { return false; } $cookieName = 'joomla_remember_me_' . UserHelper::getShortHashedUserAgent(); - $cookieValue = $this->app->getInput()->cookie->get($cookieName); + $cookieValue = $this->getApplication()->getInput()->cookie->get($cookieName); // There are no cookies to delete. if (!$cookieValue) { @@ -388,19 +376,19 @@ public function onUserAfterLogout($options) $series = $filter->clean($cookieArray[1], 'ALNUM'); // Remove the record from the database - $query = $this->db->getQuery(true) - ->delete($this->db->quoteName('#__user_keys')) - ->where($this->db->quoteName('series') . ' = :series') + $query = $this->getDatabase()->getQuery(true) + ->delete($this->getDatabase()->quoteName('#__user_keys')) + ->where($this->getDatabase()->quoteName('series') . ' = :series') ->bind(':series', $series); try { - $this->db->setQuery($query)->execute(); + $this->getDatabase()->setQuery($query)->execute(); } catch (RuntimeException $e) { // We aren't concerned with errors from this query, carry on } // Destroy the cookie - $this->app->getInput()->cookie->set($cookieName, '', 1, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain', '')); + $this->getApplication()->getInput()->cookie->set($cookieName, '', 1, $this->getApplication()->get('cookie_path', '/'), $this->getApplication()->get('cookie_domain', '')); return true; } diff --git a/plugins/authentication/joomla/joomla.xml b/plugins/authentication/joomla/joomla.xml index 64758f07785b4..da9112697185b 100644 --- a/plugins/authentication/joomla/joomla.xml +++ b/plugins/authentication/joomla/joomla.xml @@ -9,8 +9,10 @@ www.joomla.org 3.0.0 PLG_AUTHENTICATION_JOOMLA_XML_DESCRIPTION + Joomla\Plugin\Authentication\Joomla - joomla.php + services + src language/en-GB/plg_authentication_joomla.ini diff --git a/plugins/authentication/joomla/services/provider.php b/plugins/authentication/joomla/services/provider.php new file mode 100644 index 0000000000000..33feb5739a2cd --- /dev/null +++ b/plugins/authentication/joomla/services/provider.php @@ -0,0 +1,49 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Database\DatabaseInterface; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Authentication\Joomla\Extension\Joomla; + +return new class implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register(Container $container) + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new Joomla( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('authentication', 'joomla') + ); + $plugin->setApplication(Factory::getApplication()); + $plugin->setDatabase($container->get(DatabaseInterface::class)); + + return $plugin; + } + ); + } +}; diff --git a/plugins/authentication/joomla/joomla.php b/plugins/authentication/joomla/src/Extension/Joomla.php similarity index 77% rename from plugins/authentication/joomla/joomla.php rename to plugins/authentication/joomla/src/Extension/Joomla.php index 7fcb7a5b2b013..6a09d1afcce77 100644 --- a/plugins/authentication/joomla/joomla.php +++ b/plugins/authentication/joomla/src/Extension/Joomla.php @@ -6,17 +6,15 @@ * * @copyright (C) 2006 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt - - * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ +namespace Joomla\Plugin\Authentication\Joomla\Extension; + use Joomla\CMS\Authentication\Authentication; -use Joomla\CMS\Helper\AuthenticationHelper; -use Joomla\CMS\Language\Text; use Joomla\CMS\Plugin\CMSPlugin; -use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; +use Joomla\Database\DatabaseAwareTrait; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -27,23 +25,9 @@ * * @since 1.5 */ -class PlgAuthenticationJoomla extends CMSPlugin +final class Joomla extends CMSPlugin { - /** - * Application object - * - * @var \Joomla\CMS\Application\CMSApplication - * @since 4.0.0 - */ - protected $app; - - /** - * Database object - * - * @var \Joomla\Database\DatabaseDriver - * @since 4.0.0 - */ - protected $db; + use DatabaseAwareTrait; /** * This method should handle any authentication and report back to the subject @@ -63,12 +47,12 @@ public function onUserAuthenticate($credentials, $options, &$response) // Joomla does not like blank passwords if (empty($credentials['password'])) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); return; } - $db = $this->db; + $db = $this->getDatabase(); $query = $db->getQuery(true) ->select($db->quoteName(['id', 'password'])) ->from($db->quoteName('#__users')) @@ -87,7 +71,7 @@ public function onUserAuthenticate($credentials, $options, &$response) $response->email = $user->email; $response->fullname = $user->name; - if ($this->app->isClient('administrator')) { + if ($this->getApplication()->isClient('administrator')) { $response->language = $user->getParam('admin_language'); } else { $response->language = $user->getParam('language'); @@ -98,7 +82,7 @@ public function onUserAuthenticate($credentials, $options, &$response) } else { // Invalid password $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_INVALID_PASS'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS'); } } else { // Let's hash the entered password even if we don't have a matching user for some extra response time @@ -107,7 +91,7 @@ public function onUserAuthenticate($credentials, $options, &$response) // Invalid user $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_NO_USER'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER'); } } } diff --git a/plugins/authentication/ldap/ldap.xml b/plugins/authentication/ldap/ldap.xml index 2b0e4f42596c6..758a680f4684a 100644 --- a/plugins/authentication/ldap/ldap.xml +++ b/plugins/authentication/ldap/ldap.xml @@ -9,8 +9,10 @@ www.joomla.org 3.0.0 PLG_LDAP_XML_DESCRIPTION + Joomla\Plugin\Authentication\Ldap - ldap.php + services + src language/en-GB/plg_authentication_ldap.ini diff --git a/plugins/authentication/ldap/services/provider.php b/plugins/authentication/ldap/services/provider.php new file mode 100644 index 0000000000000..a345c3e2da61d --- /dev/null +++ b/plugins/authentication/ldap/services/provider.php @@ -0,0 +1,49 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\Database\DatabaseInterface; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Authentication\Ldap\Extension\Ldap; + +return new class implements ServiceProviderInterface +{ + /** + * Registers the service provider with a DI container. + * + * @param Container $container The DI container. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register(Container $container) + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new Ldap( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('authentication', 'ldap') + ); + $plugin->setApplication(Factory::getApplication()); + $plugin->setDatabase($container->get(DatabaseInterface::class)); + + return $plugin; + } + ); + } +}; diff --git a/plugins/authentication/ldap/ldap.php b/plugins/authentication/ldap/src/Extension/Ldap.php similarity index 85% rename from plugins/authentication/ldap/ldap.php rename to plugins/authentication/ldap/src/Extension/Ldap.php index 0f2679635beb9..cb281678a8da4 100644 --- a/plugins/authentication/ldap/ldap.php +++ b/plugins/authentication/ldap/src/Extension/Ldap.php @@ -10,14 +10,16 @@ * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ +namespace Joomla\Plugin\Authentication\Ldap\Extension; + use Joomla\CMS\Authentication\Authentication; -use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Database\DatabaseAwareTrait; use Symfony\Component\Ldap\Entry; use Symfony\Component\Ldap\Exception\ConnectionException; use Symfony\Component\Ldap\Exception\LdapException; -use Symfony\Component\Ldap\Ldap; +use Symfony\Component\Ldap\Ldap as LdapProvider; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -28,8 +30,10 @@ * * @since 1.5 */ -class PlgAuthenticationLdap extends CMSPlugin +final class Ldap extends CMSPlugin { + use DatabaseAwareTrait; + /** * This method should handle any authentication and report back to the subject * @@ -58,7 +62,7 @@ public function onUserAuthenticate($credentials, $options, &$response) // LDAP does not like Blank passwords (tries to Anon Bind which is bad) if (empty($credentials['password'])) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); return false; } @@ -79,10 +83,7 @@ public function onUserAuthenticate($credentials, $options, &$response) Log::add(sprintf('Creating LDAP session with options: %s', json_encode($options)), Log::DEBUG, $logcategory); $connection_string = sprintf('ldap%s://%s:%s', 'ssl' === $options['encryption'] ? 's' : '', $options['host'], $options['port']); Log::add(sprintf('Creating LDAP session to connect to "%s" while binding', $connection_string), Log::DEBUG, $logcategory); - $ldap = Ldap::create( - 'ext_ldap', - $options - ); + $ldap = LdapProvider::create('ext_ldap', $options); switch ($auth_method) { case 'search': @@ -92,7 +93,7 @@ public function onUserAuthenticate($credentials, $options, &$response) $ldap->bind($dn, $this->params->get('password', '')); } catch (ConnectionException | LdapException $exception) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_NOT_CONNECT'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NOT_CONNECT'); Log::add($exception->getMessage(), Log::ERROR, $logcategory); return; @@ -112,7 +113,7 @@ public function onUserAuthenticate($credentials, $options, &$response) ); } catch (LdapException $exception) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); Log::add($exception->getMessage(), Log::ERROR, $logcategory); return; @@ -121,8 +122,8 @@ public function onUserAuthenticate($credentials, $options, &$response) if (!$entry) { // we did not find the login in LDAP $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_NO_USER'); - Log::add(Text::_('JGLOBAL_AUTH_USER_NOT_FOUND'), Log::ERROR, $logcategory); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER'); + Log::add($this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_USER_NOT_FOUND'), Log::ERROR, $logcategory); return; } else { @@ -135,7 +136,7 @@ public function onUserAuthenticate($credentials, $options, &$response) $ldap->bind($entry->getDn(), $credentials['password']); } catch (ConnectionException $exception) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_INVALID_PASS'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS'); Log::add($exception->getMessage(), Log::ERROR, $logcategory); return; @@ -160,7 +161,7 @@ public function onUserAuthenticate($credentials, $options, &$response) $ldap->bind($dn, $credentials['password']); } catch (ConnectionException | LdapException $exception) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_INVALID_PASS'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS'); Log::add($exception->getMessage(), Log::ERROR, $logcategory); return; @@ -179,7 +180,7 @@ public function onUserAuthenticate($credentials, $options, &$response) ); } catch (LdapException $exception) { $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); Log::add($exception->getMessage(), Log::ERROR, $logcategory); return; @@ -190,7 +191,7 @@ public function onUserAuthenticate($credentials, $options, &$response) default: // Unsupported configuration $response->status = Authentication::STATUS_FAILURE; - $response->error_message = Text::_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); + $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_UNKNOWN_ACCESS_DENIED'); Log::add($response->error_message, Log::ERROR, $logcategory); return; @@ -216,14 +217,14 @@ public function onUserAuthenticate($credentials, $options, &$response) * Note that this method requires that semicolons which should be part of the search term to be escaped * to correctly split the search string into separate lookups * - * @param string $search search string of search values - * @param Ldap $ldap The LDAP client + * @param string $search search string of search values + * @param LdapProvider $ldap The LDAP client * * @return Entry|null The search result entry if a matching record was found * * @since 3.8.2 */ - private function searchByString($search, Ldap $ldap) + private function searchByString($search, LdapProvider $ldap) { $dn = $this->params->get('base_dn'); From 415fbc1521ffcc94ebcd9e48d7fc8971dbc80ed2 Mon Sep 17 00:00:00 2001 From: Allon Moritz Date: Fri, 13 Jan 2023 16:58:35 +0100 Subject: [PATCH 2/4] adapt test --- .../Authentication/Ldap/LdapPluginTest.php | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/tests/Unit/Plugin/Authentication/Ldap/LdapPluginTest.php b/tests/Unit/Plugin/Authentication/Ldap/LdapPluginTest.php index bad9351778036..ba4154a2aeceb 100644 --- a/tests/Unit/Plugin/Authentication/Ldap/LdapPluginTest.php +++ b/tests/Unit/Plugin/Authentication/Ldap/LdapPluginTest.php @@ -10,10 +10,12 @@ namespace Joomla\Tests\Unit\Plugin\Authentication\Ldap; +use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Authentication\Authentication; use Joomla\CMS\Authentication\AuthenticationResponse; -use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Language\Language; use Joomla\Event\Dispatcher; +use Joomla\Plugin\Authentication\Ldap\Extension\Ldap as LdapPlugin; use Joomla\Tests\Unit\UnitTestCase; use Symfony\Component\Ldap\Ldap; @@ -32,25 +34,43 @@ class LdapPluginTest extends UnitTestCase public const LDAPPORT = "1389"; public const SSLPORT = "1636"; - private function getPlugin($options): CMSPlugin + /** + * The default options + * + * @var array + * @since __DEPLOY_VERSION__ + */ + private $default_options; + + /** + * The default credentials + * + * @var array + * @since __DEPLOY_VERSION__ + */ + private $default_credentials; + + private function getPlugin($options): LdapPlugin { - $type = "authentication"; - $plugin = "ldap"; + $language = $this->createStub(Language::class); + $language->method('_')->willReturn('test'); - // based on loadPluginFromFilesystem in ExtensionManagerTrait - $path = JPATH_PLUGINS . '/' . $type . '/' . $plugin . '/' . $plugin . '.php'; - require_once $path; + $app = $this->createStub(CMSApplicationInterface::class); + $app->method('getLanguage')->willReturn($language); $dispatcher = new Dispatcher(); // plugin object: result from DB using PluginHelper::getPlugin - $pluginobject = [ - 'name' => $plugin, + $pluginObject = [ + 'name' => 'ldap', 'params' => json_encode($options), - 'type' => $type + 'type' => 'authentication' ]; - return new \PlgAuthenticationLdap($dispatcher, $pluginobject); + $plugin = new LdapPlugin($dispatcher, $pluginObject); + $plugin->setApplication($app); + + return $plugin; } private function acceptCertificates(): void From 558f6dda8738d816f92963fed45eaa7e6167c583 Mon Sep 17 00:00:00 2001 From: Allon Moritz Date: Fri, 13 Jan 2023 17:50:06 +0100 Subject: [PATCH 3/4] variable --- plugins/authentication/cookie/services/provider.php | 4 +++- plugins/authentication/joomla/services/provider.php | 4 +++- plugins/authentication/ldap/services/provider.php | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/authentication/cookie/services/provider.php b/plugins/authentication/cookie/services/provider.php index a77f1d4611e77..25ce4f9ebd0cf 100644 --- a/plugins/authentication/cookie/services/provider.php +++ b/plugins/authentication/cookie/services/provider.php @@ -35,8 +35,10 @@ public function register(Container $container) $container->set( PluginInterface::class, function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + $plugin = new Cookie( - $container->get(DispatcherInterface::class), + $dispatcher, (array) PluginHelper::getPlugin('authentication', 'cookie') ); $plugin->setApplication(Factory::getApplication()); diff --git a/plugins/authentication/joomla/services/provider.php b/plugins/authentication/joomla/services/provider.php index 33feb5739a2cd..52575131698ab 100644 --- a/plugins/authentication/joomla/services/provider.php +++ b/plugins/authentication/joomla/services/provider.php @@ -35,8 +35,10 @@ public function register(Container $container) $container->set( PluginInterface::class, function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + $plugin = new Joomla( - $container->get(DispatcherInterface::class), + $dispatcher, (array) PluginHelper::getPlugin('authentication', 'joomla') ); $plugin->setApplication(Factory::getApplication()); diff --git a/plugins/authentication/ldap/services/provider.php b/plugins/authentication/ldap/services/provider.php index a345c3e2da61d..dfeb4cf8296da 100644 --- a/plugins/authentication/ldap/services/provider.php +++ b/plugins/authentication/ldap/services/provider.php @@ -35,8 +35,10 @@ public function register(Container $container) $container->set( PluginInterface::class, function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + $plugin = new Ldap( - $container->get(DispatcherInterface::class), + $dispatcher, (array) PluginHelper::getPlugin('authentication', 'ldap') ); $plugin->setApplication(Factory::getApplication()); From c6c61b441535495599e398caff61415d94457611 Mon Sep 17 00:00:00 2001 From: Allon Moritz Date: Wed, 18 Jan 2023 06:44:48 +0100 Subject: [PATCH 4/4] cs --- plugins/authentication/ldap/src/Extension/Ldap.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/authentication/ldap/src/Extension/Ldap.php b/plugins/authentication/ldap/src/Extension/Ldap.php index cb281678a8da4..09f3c280b1f44 100644 --- a/plugins/authentication/ldap/src/Extension/Ldap.php +++ b/plugins/authentication/ldap/src/Extension/Ldap.php @@ -6,8 +6,6 @@ * * @copyright (C) 2006 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt - - * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ namespace Joomla\Plugin\Authentication\Ldap\Extension;