From deb3214a55ad4bde60f2afc5c259b99bb266cc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Wed, 16 Aug 2023 17:08:33 -0300 Subject: [PATCH] Create the Authentication middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: MaurĂ­cio Meneghini Fauth --- libraries/classes/Application.php | 67 +--------- .../classes/Middleware/Authentication.php | 115 ++++++++++++++++++ phpstan-baseline.neon | 30 ++--- 3 files changed, 132 insertions(+), 80 deletions(-) create mode 100644 libraries/classes/Middleware/Authentication.php diff --git a/libraries/classes/Application.php b/libraries/classes/Application.php index 8f68565b9b..fc60579425 100644 --- a/libraries/classes/Application.php +++ b/libraries/classes/Application.php @@ -7,10 +7,7 @@ namespace PhpMyAdmin; use Fig\Http\Message\StatusCodeInterface; use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Laminas\HttpHandlerRunner\RequestHandlerRunner; -use PhpMyAdmin\Config\Settings\Server; use PhpMyAdmin\ConfigStorage\Relation; -use PhpMyAdmin\Dbal\Connection; -use PhpMyAdmin\Exceptions\AuthenticationPluginException; use PhpMyAdmin\Http\Factory\ResponseFactory; use PhpMyAdmin\Http\Factory\ServerRequestFactory; use PhpMyAdmin\Http\Handler\ApplicationHandler; @@ -19,6 +16,7 @@ use PhpMyAdmin\Http\Response; use PhpMyAdmin\Http\ServerRequest; use PhpMyAdmin\Identifiers\DatabaseName; use PhpMyAdmin\Identifiers\TableName; +use PhpMyAdmin\Middleware\Authentication; use PhpMyAdmin\Middleware\ConfigErrorAndPermissionChecking; use PhpMyAdmin\Middleware\ConfigLoading; use PhpMyAdmin\Middleware\CurrentServerGlobalSetting; @@ -45,8 +43,6 @@ use PhpMyAdmin\Middleware\TokenRequestParamChecking; use PhpMyAdmin\Middleware\UriSchemeUpdating; use PhpMyAdmin\Middleware\UrlParamsSetting; use PhpMyAdmin\Middleware\UrlRedirection; -use PhpMyAdmin\Plugins\AuthenticationPlugin; -use PhpMyAdmin\Plugins\AuthenticationPluginFactory; use PhpMyAdmin\Routing\Routing; use PhpMyAdmin\SqlParser\Lexer; use PhpMyAdmin\Theme\ThemeManager; @@ -57,7 +53,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Throwable; use function __; -use function define; use function function_exists; use function hash_equals; use function is_array; @@ -128,6 +123,7 @@ class Application $requestHandler->add(new LanguageAndThemeCookieSaving($this->config)); $requestHandler->add(new DbiLoading()); $requestHandler->add(new LoginCookieValiditySetting($this->config)); + $requestHandler->add(new Authentication($this->config, $this->template, $this->responseFactory)); $runner = new RequestHandlerRunner( $requestHandler, @@ -148,8 +144,6 @@ class Application public function handle(ServerRequest $request): Response|null { - $route = $request->getRoute(); - $container = Core::getContainerBuilder(); $settings = $this->config->getSettings(); @@ -159,32 +153,6 @@ class Application $currentServer = $this->config->getCurrentServer(); if ($currentServer !== null) { - /** @var AuthenticationPluginFactory $authPluginFactory */ - $authPluginFactory = $container->get(AuthenticationPluginFactory::class); - try { - $authPlugin = $authPluginFactory->create(); - } catch (AuthenticationPluginException $exception) { - return $this->getGenericErrorResponse($exception->getMessage()); - } - - $authPlugin->authenticate(); - $currentServer = new Server($GLOBALS['cfg']['Server']); - - /* Enable LOAD DATA LOCAL INFILE for LDI plugin */ - if ($route === '/import' && ($_POST['format'] ?? '') === 'ldi') { - // Switch this before the DB connection is done - // phpcs:disable PSR1.Files.SideEffects - define('PMA_ENABLE_LDI', 1); - // phpcs:enable - } - - $this->connectToDatabaseServer($GLOBALS['dbi'], $authPlugin, $currentServer); - $authPlugin->rememberCredentials(); - $authPlugin->checkTwoFactor($request); - - /* Log success */ - Logging::logUser($this->config, $currentServer->user); - if ($GLOBALS['dbi']->getVersion() < $settings->mysqlMinVersion['internal']) { return $this->getGenericErrorResponse(sprintf( __('You should upgrade to %s %s or later.'), @@ -359,37 +327,6 @@ class Application $container->setParameter('url_params', $GLOBALS['urlParams']); } - private function connectToDatabaseServer( - DatabaseInterface $dbi, - AuthenticationPlugin $auth, - Server $currentServer, - ): void { - /** - * Try to connect MySQL with the control user profile (will be used to get the privileges list for the current - * user but the true user link must be open after this one, so it would be default one for all the scripts). - */ - $controlConnection = null; - if ($currentServer->controlUser !== '') { - $controlConnection = $dbi->connect($currentServer, Connection::TYPE_CONTROL); - } - - // Connects to the server (validates user's login) - $userConnection = $dbi->connect($currentServer, Connection::TYPE_USER); - if ($userConnection === null) { - $auth->showFailure('mysql-denied'); - } - - if ($controlConnection !== null) { - return; - } - - /** - * Open separate connection for control queries, this is needed to avoid problems with table locking used in - * main connection and phpMyAdmin issuing queries to configuration storage, which is not locked by that time. - */ - $dbi->connect($currentServer, Connection::TYPE_USER, Connection::TYPE_CONTROL); - } - private function getGenericErrorResponse(string $message): Response { $response = $this->responseFactory->createResponse(StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR); diff --git a/libraries/classes/Middleware/Authentication.php b/libraries/classes/Middleware/Authentication.php new file mode 100644 index 0000000000..a7c1ceafa1 --- /dev/null +++ b/libraries/classes/Middleware/Authentication.php @@ -0,0 +1,115 @@ +config->getCurrentServer() === null) { + return $handler->handle($request); + } + + /** @var AuthenticationPluginFactory $authPluginFactory */ + $authPluginFactory = Core::getContainerBuilder()->get(AuthenticationPluginFactory::class); + try { + $authPlugin = $authPluginFactory->create(); + } catch (AuthenticationPluginException $exception) { + $response = $this->responseFactory->createResponse(StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR); + + return $response->write($this->template->render('error/generic', [ + 'lang' => $GLOBALS['lang'] ?? 'en', + 'dir' => $GLOBALS['text_dir'] ?? 'ltr', + 'error_message' => $exception->getMessage(), + ])); + } + + try { + $authPlugin->authenticate(); + $currentServer = new Server($GLOBALS['cfg']['Server']); + + /* Enable LOAD DATA LOCAL INFILE for LDI plugin */ + if ($request->getAttribute('route') === '/import' && ($_POST['format'] ?? '') === 'ldi') { + // Switch this before the DB connection is done + // phpcs:disable PSR1.Files.SideEffects + define('PMA_ENABLE_LDI', 1); + // phpcs:enable + } + + $this->connectToDatabaseServer($GLOBALS['dbi'], $authPlugin, $currentServer); + $authPlugin->rememberCredentials(); + assert($request instanceof ServerRequest); + $authPlugin->checkTwoFactor($request); + } catch (ExitException) { + return ResponseRenderer::getInstance()->response(); + } + + /* Log success */ + Logging::logUser($this->config, $currentServer->user); + + return $handler->handle($request); + } + + private function connectToDatabaseServer( + DatabaseInterface $dbi, + AuthenticationPlugin $auth, + Server $currentServer, + ): void { + /** + * Try to connect MySQL with the control user profile (will be used to get the privileges list for the current + * user but the true user link must be open after this one, so it would be default one for all the scripts). + */ + $controlConnection = null; + if ($currentServer->controlUser !== '') { + $controlConnection = $dbi->connect($currentServer, Connection::TYPE_CONTROL); + } + + // Connects to the server (validates user's login) + $userConnection = $dbi->connect($currentServer, Connection::TYPE_USER); + if ($userConnection === null) { + $auth->showFailure('mysql-denied'); + } + + if ($controlConnection !== null) { + return; + } + + /** + * Open separate connection for control queries, this is needed to avoid problems with table locking used in + * main connection and phpMyAdmin issuing queries to configuration storage, which is not locked by that time. + */ + $dbi->connect($currentServer, Connection::TYPE_USER, Connection::TYPE_CONTROL); + } +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3258cebe4f..89523a3836 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -45,11 +45,6 @@ parameters: count: 1 path: libraries/classes/Advisory/Advisor.php - - - message: "#^Cannot access offset 'Server' on mixed\\.$#" - count: 1 - path: libraries/classes/Application.php - - message: "#^Cannot access offset 'table' on mixed\\.$#" count: 1 @@ -70,11 +65,6 @@ parameters: count: 1 path: libraries/classes/Application.php - - - message: "#^Parameter \\#1 \\$dbi of method PhpMyAdmin\\\\Application\\:\\:connectToDatabaseServer\\(\\) expects PhpMyAdmin\\\\DatabaseInterface, mixed given\\.$#" - count: 1 - path: libraries/classes/Application.php - - message: "#^Parameter \\#1 \\$dbi of static method PhpMyAdmin\\\\Profiling\\:\\:check\\(\\) expects PhpMyAdmin\\\\DatabaseInterface, mixed given\\.$#" count: 1 @@ -85,11 +75,6 @@ parameters: count: 1 path: libraries/classes/Application.php - - - message: "#^Parameter \\#1 \\$server of class PhpMyAdmin\\\\Config\\\\Settings\\\\Server constructor expects array\\, mixed given\\.$#" - count: 1 - path: libraries/classes/Application.php - - message: "#^Parameter \\#2 \\$value of method Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\:\\:setParameter\\(\\) expects array\\|bool\\|float\\|int\\|string\\|UnitEnum\\|null, mixed given\\.$#" count: 1 @@ -15815,6 +15800,21 @@ parameters: count: 1 path: libraries/classes/Message.php + - + message: "#^Cannot access offset 'Server' on mixed\\.$#" + count: 1 + path: libraries/classes/Middleware/Authentication.php + + - + message: "#^Parameter \\#1 \\$dbi of method PhpMyAdmin\\\\Middleware\\\\Authentication\\:\\:connectToDatabaseServer\\(\\) expects PhpMyAdmin\\\\DatabaseInterface, mixed given\\.$#" + count: 1 + path: libraries/classes/Middleware/Authentication.php + + - + message: "#^Parameter \\#1 \\$server of class PhpMyAdmin\\\\Config\\\\Settings\\\\Server constructor expects array\\, mixed given\\.$#" + count: 1 + path: libraries/classes/Middleware/Authentication.php + - message: "#^Parameter \\#1 \\$source of method PhpMyAdmin\\\\Config\\:\\:loadAndCheck\\(\\) expects string\\|null, mixed given\\.$#" count: 1