Create the Authentication middleware

Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
This commit is contained in:
Maurício Meneghini Fauth 2023-08-16 17:08:33 -03:00
parent 73cb80c785
commit deb3214a55
No known key found for this signature in database
GPG Key ID: 6A16FD38AFC89CC8
3 changed files with 132 additions and 80 deletions

View File

@ -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);

View File

@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Middleware;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\Settings\Server;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Exceptions\AuthenticationPluginException;
use PhpMyAdmin\Exceptions\ExitException;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Logging;
use PhpMyAdmin\Plugins\AuthenticationPlugin;
use PhpMyAdmin\Plugins\AuthenticationPluginFactory;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
use function define;
final class Authentication implements MiddlewareInterface
{
public function __construct(
private readonly Config $config,
private readonly Template $template,
private readonly ResponseFactory $responseFactory,
) {
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($this->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);
}
}

View File

@ -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\\<int\\|string, mixed\\>, 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\\<int\\|string, mixed\\>, 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