Merge pull request #19154 from MauricioFauth/auth-plugin-response-handling
Remove ResponseRenderer::callExit() calls from auth plugins
This commit is contained in:
commit
b93d1b9b52
@ -2235,9 +2235,10 @@ logs. Currently there are two variables available:
|
||||
User name of currently active user (they do not have to be logged in).
|
||||
``userStatus``
|
||||
Status of currently active user, one of ``ok`` (user is logged in),
|
||||
``mysql-denied`` (MySQL denied user login), ``allow-denied`` (user denied
|
||||
``server-denied`` (database server denied user login), ``allow-denied`` (user denied
|
||||
by allow/deny rules), ``root-denied`` (root is denied in configuration),
|
||||
``empty-denied`` (empty password is denied).
|
||||
``empty-denied`` (empty password is denied),
|
||||
``no-activity`` (automatically logged out due to inactivity).
|
||||
|
||||
``LogFormat`` directive for Apache can look like following:
|
||||
|
||||
|
||||
@ -7672,6 +7672,7 @@
|
||||
<code><![CDATA[$password]]></code>
|
||||
</PossiblyInvalidPropertyAssignmentValue>
|
||||
<PossiblyUnusedReturnValue>
|
||||
<code><![CDATA[Response]]></code>
|
||||
<code><![CDATA[bool]]></code>
|
||||
</PossiblyUnusedReturnValue>
|
||||
<RedundantCast>
|
||||
@ -7761,6 +7762,9 @@
|
||||
<code><![CDATA[$config->selectedServer]]></code>
|
||||
<code><![CDATA[array_merge($config->selectedServer, $singleSignonCfgUpdate)]]></code>
|
||||
</MixedPropertyTypeCoercion>
|
||||
<PossiblyUnusedReturnValue>
|
||||
<code><![CDATA[Response]]></code>
|
||||
</PossiblyUnusedReturnValue>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<code><![CDATA[empty($config->selectedServer['SignonURL'])]]></code>
|
||||
</RiskyTruthyFalsyComparison>
|
||||
@ -14310,6 +14314,7 @@
|
||||
<code><![CDATA[$config->settings]]></code>
|
||||
<code><![CDATA[$config->settings]]></code>
|
||||
<code><![CDATA[$config->settings]]></code>
|
||||
<code><![CDATA[$config->settings]]></code>
|
||||
</PropertyTypeCoercion>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[assertSame]]></code>
|
||||
|
||||
78
src/Exceptions/AuthenticationFailure.php
Normal file
78
src/Exceptions/AuthenticationFailure.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
use function __;
|
||||
|
||||
final class AuthenticationFailure extends RuntimeException
|
||||
{
|
||||
public const SERVER_DENIED = 'server-denied';
|
||||
public const ALLOW_DENIED = 'allow-denied';
|
||||
public const ROOT_DENIED = 'root-denied';
|
||||
public const EMPTY_DENIED = 'empty-denied';
|
||||
public const NO_ACTIVITY = 'no-activity';
|
||||
|
||||
/** @psalm-param self::* $failureType */
|
||||
public function __construct(
|
||||
public readonly string $failureType,
|
||||
string $message = '',
|
||||
int $code = 0,
|
||||
Throwable|null $previous = null,
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Database server denied user login
|
||||
*/
|
||||
public static function deniedByDatabaseServer(): self
|
||||
{
|
||||
return new self(self::SERVER_DENIED, __('Cannot log in to the database server.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* User denied by allow/deny rules
|
||||
*/
|
||||
public static function deniedByAllowDenyRules(): self
|
||||
{
|
||||
return new self(self::ALLOW_DENIED, __('Access denied!'));
|
||||
}
|
||||
|
||||
/**
|
||||
* User 'root' is denied in configuration
|
||||
*/
|
||||
public static function rootDeniedByConfiguration(): self
|
||||
{
|
||||
return new self(self::ROOT_DENIED, __('Access denied!'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty password is denied
|
||||
*/
|
||||
public static function emptyPasswordDeniedByConfiguration(): self
|
||||
{
|
||||
return new self(
|
||||
self::EMPTY_DENIED,
|
||||
__('Login without a password is forbidden by configuration (see AllowNoPassword).'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically logged out due to inactivity
|
||||
*/
|
||||
public static function loggedOutDueToInactivity(): self
|
||||
{
|
||||
return new self(
|
||||
self::NO_ACTIVITY,
|
||||
__(
|
||||
'You have been automatically logged out due to inactivity of %s seconds.'
|
||||
. ' Once you log in again, you should be able to resume the work where you left off.',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -11,13 +11,13 @@ use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Container\ContainerBuilder;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\ConnectionType;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationPluginException;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Http\Factory\ResponseFactory;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\LanguageManager;
|
||||
use PhpMyAdmin\Logging;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPluginFactory;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
@ -26,6 +26,7 @@ use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Throwable;
|
||||
|
||||
use function assert;
|
||||
use function define;
|
||||
@ -60,7 +61,23 @@ final class Authentication implements MiddlewareInterface
|
||||
}
|
||||
|
||||
try {
|
||||
$authPlugin->authenticate();
|
||||
try {
|
||||
$response = $authPlugin->authenticate();
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
} catch (AuthenticationFailure $exception) {
|
||||
return $authPlugin->showFailure($exception);
|
||||
} catch (Throwable $exception) {
|
||||
$response = $this->responseFactory->createResponse(StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR);
|
||||
|
||||
return $response->write($this->template->render('error/generic', [
|
||||
'lang' => $GLOBALS['lang'] ?? 'en',
|
||||
'dir' => LanguageManager::$textDir,
|
||||
'error_message' => $exception->getMessage(),
|
||||
]));
|
||||
}
|
||||
|
||||
$currentServer = new Server(Config::getInstance()->selectedServer);
|
||||
|
||||
/* Enable LOAD DATA LOCAL INFILE for LDI plugin */
|
||||
@ -71,7 +88,11 @@ final class Authentication implements MiddlewareInterface
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
$this->connectToDatabaseServer(DatabaseInterface::getInstance(), $authPlugin, $currentServer);
|
||||
try {
|
||||
$this->connectToDatabaseServer(DatabaseInterface::getInstance(), $currentServer);
|
||||
} catch (AuthenticationFailure $exception) {
|
||||
return $authPlugin->showFailure($exception);
|
||||
}
|
||||
|
||||
// Relation should only be initialized after the connection is successful
|
||||
/** @var Relation $relation */
|
||||
@ -81,9 +102,16 @@ final class Authentication implements MiddlewareInterface
|
||||
// Tracker can only be activated after the relation has been initialized
|
||||
Tracker::enable();
|
||||
|
||||
$authPlugin->rememberCredentials();
|
||||
$response = $authPlugin->rememberCredentials();
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
assert($request instanceof ServerRequest);
|
||||
$authPlugin->checkTwoFactor($request);
|
||||
$response = $authPlugin->checkTwoFactor($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
} catch (ExitException) {
|
||||
return ResponseRenderer::getInstance()->response();
|
||||
}
|
||||
@ -94,11 +122,9 @@ final class Authentication implements MiddlewareInterface
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
private function connectToDatabaseServer(
|
||||
DatabaseInterface $dbi,
|
||||
AuthenticationPlugin $auth,
|
||||
Server $currentServer,
|
||||
): void {
|
||||
/** @throws AuthenticationFailure */
|
||||
private function connectToDatabaseServer(DatabaseInterface $dbi, 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).
|
||||
@ -111,7 +137,7 @@ final class Authentication implements MiddlewareInterface
|
||||
// Connects to the server (validates user's login)
|
||||
$userConnection = $dbi->connect($currentServer, ConnectionType::User);
|
||||
if ($userConnection === null) {
|
||||
$auth->showFailure('mysql-denied');
|
||||
throw AuthenticationFailure::deniedByDatabaseServer();
|
||||
}
|
||||
|
||||
if ($controlConnection !== null) {
|
||||
|
||||
@ -10,7 +10,9 @@ namespace PhpMyAdmin\Plugins\Auth;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Error\ErrorHandler;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Select;
|
||||
@ -18,6 +20,8 @@ use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function count;
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use function sprintf;
|
||||
use function trigger_error;
|
||||
|
||||
@ -32,17 +36,18 @@ class AuthenticationConfig extends AuthenticationPlugin
|
||||
/**
|
||||
* Displays authentication form
|
||||
*/
|
||||
public function showLoginForm(): void
|
||||
public function showLoginForm(): Response|null
|
||||
{
|
||||
$response = ResponseRenderer::getInstance();
|
||||
if (! $response->isAjax()) {
|
||||
return;
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
if (! $responseRenderer->isAjax()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$response->setRequestStatus(false);
|
||||
$responseRenderer->setRequestStatus(false);
|
||||
// reload_flag removes the token parameter from the URL and reloads
|
||||
$response->addJSON('reload_flag', '1');
|
||||
$response->callExit();
|
||||
$responseRenderer->addJSON('reload_flag', '1');
|
||||
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,12 +70,10 @@ class AuthenticationConfig extends AuthenticationPlugin
|
||||
|
||||
/**
|
||||
* User is not allowed to login to MySQL -> authentication failed
|
||||
*
|
||||
* @param string $failure String describing why authentication has failed
|
||||
*/
|
||||
public function showFailure(string $failure): never
|
||||
public function showFailure(AuthenticationFailure $failure): Response
|
||||
{
|
||||
parent::showFailure($failure);
|
||||
$this->logFailure($failure);
|
||||
|
||||
$connError = DatabaseInterface::getInstance()->getError();
|
||||
if ($connError === '' || $connError === '0') {
|
||||
@ -78,12 +81,14 @@ class AuthenticationConfig extends AuthenticationPlugin
|
||||
}
|
||||
|
||||
/* HTML header */
|
||||
$response = ResponseRenderer::getInstance();
|
||||
$response->setMinimalFooter();
|
||||
$header = $response->getHeader();
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->setMinimalFooter();
|
||||
$header = $responseRenderer->getHeader();
|
||||
$header->setBodyId('loginform');
|
||||
$header->setTitle(__('Access denied!'));
|
||||
$header->disableMenuAndConsole();
|
||||
|
||||
ob_start();
|
||||
echo '<br><br>
|
||||
<div class="text-center">
|
||||
<h1>';
|
||||
@ -95,8 +100,8 @@ class AuthenticationConfig extends AuthenticationPlugin
|
||||
<tr>
|
||||
<td>';
|
||||
$config = Config::getInstance();
|
||||
if ($failure === 'allow-denied') {
|
||||
trigger_error(__('Access denied!'), E_USER_NOTICE);
|
||||
if ($failure->failureType === AuthenticationFailure::ALLOW_DENIED) {
|
||||
trigger_error($failure->getMessage(), E_USER_NOTICE);
|
||||
} else {
|
||||
// Check whether user has configured something
|
||||
if ($config->sourceMtime == 0) {
|
||||
@ -158,6 +163,9 @@ class AuthenticationConfig extends AuthenticationPlugin
|
||||
}
|
||||
|
||||
echo '</table>' , "\n";
|
||||
$response->callExit();
|
||||
|
||||
$responseRenderer->addHTML((string) ob_get_clean());
|
||||
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,14 +11,15 @@ use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\Error\ErrorHandler;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Exceptions\SessionHandlerException;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\LanguageManager;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Select;
|
||||
use PhpMyAdmin\Session;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\SessionCache;
|
||||
@ -63,11 +64,11 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
*
|
||||
* @global string $conn_error the last connection error
|
||||
*/
|
||||
public function showLoginForm(): never
|
||||
public function showLoginForm(): Response
|
||||
{
|
||||
$GLOBALS['conn_error'] ??= null;
|
||||
|
||||
$response = ResponseRenderer::getInstance();
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
|
||||
/**
|
||||
* When sending login modal after session has expired, send the
|
||||
@ -75,8 +76,8 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
* in all the forms having a hidden token.
|
||||
*/
|
||||
$sessionExpired = isset($_REQUEST['check_timeout']) || isset($_REQUEST['session_timedout']);
|
||||
if (! $sessionExpired && $response->loginPage()) {
|
||||
$response->callExit();
|
||||
if (! $sessionExpired && $responseRenderer->loginPage()) {
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,8 +86,8 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
* in all the forms having a hidden token.
|
||||
*/
|
||||
if ($sessionExpired) {
|
||||
$response->setRequestStatus(false);
|
||||
$response->addJSON('new_token', $_SESSION[' PMA_token ']);
|
||||
$responseRenderer->setRequestStatus(false);
|
||||
$responseRenderer->addJSON('new_token', $_SESSION[' PMA_token ']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,7 +95,7 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
* using the modal was successful after session expiration.
|
||||
*/
|
||||
if (isset($_REQUEST['session_timedout'])) {
|
||||
$response->addJSON('logged_in', 0);
|
||||
$responseRenderer->addJSON('logged_in', 0);
|
||||
}
|
||||
|
||||
$config = Config::getInstance();
|
||||
@ -159,7 +160,7 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
|
||||
$configFooter = Config::renderFooter();
|
||||
|
||||
$response->addHTML($this->template->render('login/form', [
|
||||
$responseRenderer->addHTML($this->template->render('login/form', [
|
||||
'login_header' => $loginHeader,
|
||||
'is_demo' => $config->config->debug->demo,
|
||||
'error_messages' => $errorMessages,
|
||||
@ -190,7 +191,7 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
'config_footer' => $configFooter,
|
||||
]));
|
||||
|
||||
$response->callExit();
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,6 +207,9 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
* it returns true if all seems ok which usually leads to auth_set_user()
|
||||
*
|
||||
* it directly switches to showFailure() if user inactivity timeout is reached
|
||||
*
|
||||
* @throws AuthenticationFailure
|
||||
* @throws SessionHandlerException
|
||||
*/
|
||||
public function readCredentials(): bool
|
||||
{
|
||||
@ -309,19 +313,8 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
$GLOBALS['pma_auth_server'] = Core::sanitizeMySQLHost($_REQUEST['pma_servername']);
|
||||
}
|
||||
|
||||
try {
|
||||
/* Secure current session on login to avoid session fixation */
|
||||
Session::secure();
|
||||
} catch (SessionHandlerException $exception) {
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->addHTML((new Template())->render('error/generic', [
|
||||
'lang' => $GLOBALS['lang'] ?? 'en',
|
||||
'dir' => LanguageManager::$textDir,
|
||||
'error_message' => $exception->getMessage(),
|
||||
]));
|
||||
|
||||
$responseRenderer->callExit();
|
||||
}
|
||||
/* Secure current session on login to avoid session fixation */
|
||||
Session::secure();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -371,7 +364,7 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
SessionCache::remove('table_priv');
|
||||
SessionCache::remove('proc_priv');
|
||||
|
||||
$this->showFailure('no-activity');
|
||||
throw AuthenticationFailure::loggedOutDueToInactivity();
|
||||
}
|
||||
|
||||
// check password cookie
|
||||
@ -440,7 +433,7 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
/**
|
||||
* Stores user credentials after successful login.
|
||||
*/
|
||||
public function rememberCredentials(): void
|
||||
public function rememberCredentials(): Response|null
|
||||
{
|
||||
// Name and password cookies need to be refreshed each time
|
||||
// Duration = one month for username
|
||||
@ -465,18 +458,18 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
|
||||
// user logged in successfully after session expiration
|
||||
if (isset($_REQUEST['session_timedout'])) {
|
||||
$response = ResponseRenderer::getInstance();
|
||||
$response->addJSON('logged_in', 1);
|
||||
$response->addJSON('success', 1);
|
||||
$response->addJSON('new_token', $_SESSION[' PMA_token ']);
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->addJSON('logged_in', 1);
|
||||
$responseRenderer->addJSON('success', 1);
|
||||
$responseRenderer->addJSON('new_token', $_SESSION[' PMA_token ']);
|
||||
|
||||
$response->callExit();
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
// Set server cookies if required (once per session) and, in this case,
|
||||
// force reload to ensure the client accepts cookies
|
||||
if ($GLOBALS['from_cookie']) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -484,11 +477,11 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
*/
|
||||
Util::clearUserCache();
|
||||
|
||||
$response = ResponseRenderer::getInstance();
|
||||
$response->disable();
|
||||
$response->redirect('./index.php?route=/' . Url::getCommonRaw($urlParams, '&'));
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->disable();
|
||||
$responseRenderer->redirect('./index.php?route=/' . Url::getCommonRaw($urlParams, '&'));
|
||||
|
||||
$response->callExit();
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -539,27 +532,23 @@ class AuthenticationCookie extends AuthenticationPlugin
|
||||
*
|
||||
* prepares error message and switches to showLoginForm() which display the error
|
||||
* and the login form
|
||||
*
|
||||
* @param string $failure String describing why authentication has failed
|
||||
*/
|
||||
public function showFailure(string $failure): never
|
||||
public function showFailure(AuthenticationFailure $failure): Response
|
||||
{
|
||||
$GLOBALS['conn_error'] ??= null;
|
||||
|
||||
parent::showFailure($failure);
|
||||
$this->logFailure($failure);
|
||||
|
||||
// Deletes password cookie and displays the login form
|
||||
Config::getInstance()->removeCookie('pmaAuth-' . Current::$server);
|
||||
|
||||
$GLOBALS['conn_error'] = $this->getErrorMessage($failure);
|
||||
|
||||
$response = ResponseRenderer::getInstance();
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
|
||||
// needed for PHP-CGI (not need for FastCGI or mod-php)
|
||||
$response->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
||||
$response->addHeader('Pragma', 'no-cache');
|
||||
$responseRenderer->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
||||
$responseRenderer->addHeader('Pragma', 'no-cache');
|
||||
|
||||
$this->showLoginForm();
|
||||
return $this->showLoginForm();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -12,6 +12,8 @@ use Fig\Http\Message\StatusCodeInterface;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\LanguageManager;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
@ -34,23 +36,24 @@ class AuthenticationHttp extends AuthenticationPlugin
|
||||
/**
|
||||
* Displays authentication form and redirect as necessary
|
||||
*/
|
||||
public function showLoginForm(): never
|
||||
public function showLoginForm(): Response
|
||||
{
|
||||
$response = ResponseRenderer::getInstance();
|
||||
if ($response->isAjax()) {
|
||||
$response->setRequestStatus(false);
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
if ($responseRenderer->isAjax()) {
|
||||
$responseRenderer->setRequestStatus(false);
|
||||
// reload_flag removes the token parameter from the URL and reloads
|
||||
$response->addJSON('reload_flag', '1');
|
||||
$response->callExit();
|
||||
$responseRenderer->addJSON('reload_flag', '1');
|
||||
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
$this->authForm();
|
||||
return $this->authForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays authentication form
|
||||
*/
|
||||
public function authForm(): never
|
||||
public function authForm(): Response
|
||||
{
|
||||
$config = Config::getInstance();
|
||||
if (empty($config->selectedServer['auth_http_realm'])) {
|
||||
@ -92,7 +95,7 @@ class AuthenticationHttp extends AuthenticationPlugin
|
||||
|
||||
$response->addHTML(Config::renderFooter());
|
||||
|
||||
$response->callExit();
|
||||
return $response->response();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,25 +181,24 @@ class AuthenticationHttp extends AuthenticationPlugin
|
||||
|
||||
/**
|
||||
* User is not allowed to login to MySQL -> authentication failed
|
||||
*
|
||||
* @param string $failure String describing why authentication has failed
|
||||
*/
|
||||
public function showFailure(string $failure): never
|
||||
public function showFailure(AuthenticationFailure $failure): Response
|
||||
{
|
||||
parent::showFailure($failure);
|
||||
$this->logFailure($failure);
|
||||
|
||||
$error = DatabaseInterface::getInstance()->getError();
|
||||
if ($error && $GLOBALS['errno'] != 1045) {
|
||||
echo $this->template->render('error/generic', [
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->addHTML($this->template->render('error/generic', [
|
||||
'lang' => $GLOBALS['lang'] ?? 'en',
|
||||
'dir' => LanguageManager::$textDir,
|
||||
'error_message' => $error,
|
||||
]);
|
||||
]));
|
||||
|
||||
ResponseRenderer::getInstance()->callExit();
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
$this->authForm();
|
||||
return $this->authForm();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,10 +8,13 @@ declare(strict_types=1);
|
||||
namespace PhpMyAdmin\Plugins\Auth;
|
||||
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\LanguageManager;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Util;
|
||||
use RuntimeException;
|
||||
|
||||
use function __;
|
||||
use function array_merge;
|
||||
@ -24,6 +27,7 @@ use function session_name;
|
||||
use function session_set_cookie_params;
|
||||
use function session_start;
|
||||
use function session_write_close;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Handles the SignOn authentication method
|
||||
@ -33,25 +37,25 @@ class AuthenticationSignon extends AuthenticationPlugin
|
||||
/**
|
||||
* Displays authentication form
|
||||
*/
|
||||
public function showLoginForm(): never
|
||||
public function showLoginForm(): Response
|
||||
{
|
||||
$response = ResponseRenderer::getInstance();
|
||||
$response->disable();
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->disable();
|
||||
unset($_SESSION['LAST_SIGNON_URL']);
|
||||
$config = Config::getInstance();
|
||||
if (empty($config->selectedServer['SignonURL'])) {
|
||||
echo $this->template->render('error/generic', [
|
||||
$responseRenderer->addHTML($this->template->render('error/generic', [
|
||||
'lang' => $GLOBALS['lang'] ?? 'en',
|
||||
'dir' => LanguageManager::$textDir,
|
||||
'error_message' => 'You must set SignonURL!',
|
||||
]);
|
||||
]));
|
||||
|
||||
$response->callExit();
|
||||
} else {
|
||||
$response->redirect($config->selectedServer['SignonURL']);
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
$response->callExit();
|
||||
$responseRenderer->redirect($config->selectedServer['SignonURL']);
|
||||
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,6 +94,8 @@ class AuthenticationSignon extends AuthenticationPlugin
|
||||
|
||||
/**
|
||||
* Gets authentication credentials
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function readCredentials(): bool
|
||||
{
|
||||
@ -118,13 +124,10 @@ class AuthenticationSignon extends AuthenticationPlugin
|
||||
/* Handle script based auth */
|
||||
if ($scriptName !== '') {
|
||||
if (! @file_exists($scriptName)) {
|
||||
echo $this->template->render('error/generic', [
|
||||
'lang' => $GLOBALS['lang'] ?? 'en',
|
||||
'dir' => LanguageManager::$textDir,
|
||||
'error_message' => __('Can not find signon authentication script:') . ' ' . $scriptName,
|
||||
]);
|
||||
|
||||
ResponseRenderer::getInstance()->callExit();
|
||||
throw new RuntimeException(sprintf(
|
||||
__('Can not find signon authentication script: %s'),
|
||||
'$cfg[\'Servers\'][$i][\'SignonScript\']',
|
||||
));
|
||||
}
|
||||
|
||||
include $scriptName;
|
||||
@ -233,12 +236,10 @@ class AuthenticationSignon extends AuthenticationPlugin
|
||||
|
||||
/**
|
||||
* User is not allowed to login to MySQL -> authentication failed
|
||||
*
|
||||
* @param string $failure String describing why authentication has failed
|
||||
*/
|
||||
public function showFailure(string $failure): never
|
||||
public function showFailure(AuthenticationFailure $failure): Response
|
||||
{
|
||||
parent::showFailure($failure);
|
||||
$this->logFailure($failure);
|
||||
|
||||
/* Session name */
|
||||
$sessionName = Config::getInstance()->selectedServer['SignonSession'];
|
||||
@ -260,7 +261,7 @@ class AuthenticationSignon extends AuthenticationPlugin
|
||||
$_SESSION['PMA_single_signon_error_message'] = $this->getErrorMessage($failure);
|
||||
}
|
||||
|
||||
$this->showLoginForm();
|
||||
return $this->showLoginForm();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract class for the authentication plugins
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Plugins;
|
||||
|
||||
use Exception;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Exceptions\SessionHandlerException;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\IpAllowDeny;
|
||||
use PhpMyAdmin\LanguageManager;
|
||||
use PhpMyAdmin\Logging;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
@ -35,8 +32,7 @@ use function sprintf;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* Provides a common interface that will have to be implemented by all of the
|
||||
* authentication plugins.
|
||||
* Provides a common interface that will have to be implemented by all the authentication plugins.
|
||||
*/
|
||||
abstract class AuthenticationPlugin
|
||||
{
|
||||
@ -63,10 +59,13 @@ abstract class AuthenticationPlugin
|
||||
/**
|
||||
* Displays authentication form
|
||||
*/
|
||||
abstract public function showLoginForm(): void;
|
||||
abstract public function showLoginForm(): Response|null;
|
||||
|
||||
/**
|
||||
* Gets authentication credentials
|
||||
*
|
||||
* @throws AuthenticationFailure
|
||||
* @throws Exception
|
||||
*/
|
||||
abstract public function readCredentials(): bool;
|
||||
|
||||
@ -87,18 +86,19 @@ abstract class AuthenticationPlugin
|
||||
/**
|
||||
* Stores user credentials after successful login.
|
||||
*/
|
||||
public function rememberCredentials(): void
|
||||
public function rememberCredentials(): Response|null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* User is not allowed to login to MySQL -> authentication failed
|
||||
*
|
||||
* @param string $failure String describing why authentication has failed
|
||||
*/
|
||||
public function showFailure(string $failure): void
|
||||
abstract public function showFailure(AuthenticationFailure $failure): Response;
|
||||
|
||||
protected function logFailure(AuthenticationFailure $failure): void
|
||||
{
|
||||
Logging::logUser(Config::getInstance(), $this->user, $failure);
|
||||
Logging::logUser(Config::getInstance(), $this->user, $failure->failureType);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,38 +157,25 @@ abstract class AuthenticationPlugin
|
||||
|
||||
/**
|
||||
* Returns error message for failed authentication.
|
||||
*
|
||||
* @param string $failure String describing why authentication has failed
|
||||
*/
|
||||
public function getErrorMessage(string $failure): string
|
||||
public function getErrorMessage(AuthenticationFailure $failure): string
|
||||
{
|
||||
if ($failure === 'empty-denied') {
|
||||
return __('Login without a password is forbidden by configuration (see AllowNoPassword)');
|
||||
if ($failure->failureType === AuthenticationFailure::NO_ACTIVITY) {
|
||||
return sprintf($failure->getMessage(), (int) Config::getInstance()->settings['LoginCookieValidity']);
|
||||
}
|
||||
|
||||
if ($failure === 'root-denied' || $failure === 'allow-denied') {
|
||||
return __('Access denied!');
|
||||
if ($failure->failureType === AuthenticationFailure::SERVER_DENIED) {
|
||||
$dbiError = DatabaseInterface::getInstance()->getError();
|
||||
if ($dbiError !== '') {
|
||||
return htmlspecialchars($dbiError);
|
||||
}
|
||||
|
||||
if (isset($GLOBALS['errno'])) {
|
||||
return '#' . $GLOBALS['errno'] . ' ' . $failure->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if ($failure === 'no-activity') {
|
||||
return sprintf(
|
||||
__('You have been automatically logged out due to inactivity of %s seconds.'
|
||||
. ' Once you log in again, you should be able to resume the work where you left off.'),
|
||||
(int) Config::getInstance()->settings['LoginCookieValidity'],
|
||||
);
|
||||
}
|
||||
|
||||
$dbiError = DatabaseInterface::getInstance()->getError();
|
||||
if ($dbiError !== '') {
|
||||
return htmlspecialchars($dbiError);
|
||||
}
|
||||
|
||||
if (isset($GLOBALS['errno'])) {
|
||||
return '#' . $GLOBALS['errno'] . ' '
|
||||
. __('Cannot log in to the MySQL server');
|
||||
}
|
||||
|
||||
return __('Cannot log in to the MySQL server');
|
||||
return $failure->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -235,28 +222,23 @@ abstract class AuthenticationPlugin
|
||||
* High level authentication interface
|
||||
*
|
||||
* Gets the credentials or shows login form if necessary
|
||||
*
|
||||
* @throws AuthenticationFailure
|
||||
* @throws Exception
|
||||
*/
|
||||
public function authenticate(): void
|
||||
public function authenticate(): Response|null
|
||||
{
|
||||
$success = $this->readCredentials();
|
||||
|
||||
/* Show login form (this exits) */
|
||||
if (! $success) {
|
||||
/* Force generating of new session */
|
||||
try {
|
||||
Session::secure();
|
||||
} catch (SessionHandlerException $exception) {
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->addHTML((new Template())->render('error/generic', [
|
||||
'lang' => $GLOBALS['lang'] ?? 'en',
|
||||
'dir' => LanguageManager::$textDir,
|
||||
'error_message' => $exception->getMessage(),
|
||||
]));
|
||||
Session::secure();
|
||||
|
||||
$responseRenderer->callExit();
|
||||
$response = $this->showLoginForm();
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$this->showLoginForm();
|
||||
}
|
||||
|
||||
/* Store credentials (eg. in cookies) */
|
||||
@ -265,10 +247,14 @@ abstract class AuthenticationPlugin
|
||||
$this->checkRules();
|
||||
/* clear user cache */
|
||||
Util::clearUserCache();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check configuration defined restrictions for authentication
|
||||
*
|
||||
* @throws AuthenticationFailure
|
||||
*/
|
||||
public function checkRules(): void
|
||||
{
|
||||
@ -287,13 +273,13 @@ abstract class AuthenticationPlugin
|
||||
|
||||
// Ejects the user if banished
|
||||
if ($allowDenyForbidden) {
|
||||
$this->showFailure('allow-denied');
|
||||
throw AuthenticationFailure::deniedByAllowDenyRules();
|
||||
}
|
||||
}
|
||||
|
||||
// is root allowed?
|
||||
if (! $config->selectedServer['AllowRoot'] && $config->selectedServer['user'] === 'root') {
|
||||
$this->showFailure('root-denied');
|
||||
throw AuthenticationFailure::rootDeniedByConfiguration();
|
||||
}
|
||||
|
||||
// is a login without password allowed?
|
||||
@ -301,39 +287,37 @@ abstract class AuthenticationPlugin
|
||||
return;
|
||||
}
|
||||
|
||||
$this->showFailure('empty-denied');
|
||||
throw AuthenticationFailure::emptyPasswordDeniedByConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two factor authentication is active
|
||||
* for given user and performs it.
|
||||
*
|
||||
* @throws ExitException
|
||||
* Checks whether two-factor authentication is active for given user and performs it.
|
||||
*/
|
||||
public function checkTwoFactor(ServerRequest $request): void
|
||||
public function checkTwoFactor(ServerRequest $request): Response|null
|
||||
{
|
||||
$twofactor = new TwoFactor($this->user);
|
||||
|
||||
/* Do we need to show the form? */
|
||||
if ($twofactor->check($request)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$response = ResponseRenderer::getInstance();
|
||||
if ($response->loginPage()) {
|
||||
$response->callExit();
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
if ($responseRenderer->loginPage()) {
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
|
||||
$response->addHTML($this->template->render('login/header', ['session_expired' => false]));
|
||||
$response->addHTML(Message::rawNotice(
|
||||
$responseRenderer->addHTML($this->template->render('login/header', ['session_expired' => false]));
|
||||
$responseRenderer->addHTML(Message::rawNotice(
|
||||
__('You have enabled two factor authentication, please confirm your login.'),
|
||||
)->getDisplay());
|
||||
$response->addHTML($this->template->render('login/twofactor', [
|
||||
$responseRenderer->addHTML($this->template->render('login/twofactor', [
|
||||
'form' => $twofactor->render($request),
|
||||
'show_submit' => $twofactor->showSubmit(),
|
||||
]));
|
||||
$response->addHTML($this->template->render('login/footer'));
|
||||
$response->addHTML(Config::renderFooter());
|
||||
$response->callExit();
|
||||
$responseRenderer->addHTML($this->template->render('login/footer'));
|
||||
$responseRenderer->addHTML(Config::renderFooter());
|
||||
|
||||
return $responseRenderer->response();
|
||||
}
|
||||
}
|
||||
|
||||
55
tests/unit/Exceptions/AuthenticationFailureTest.php
Normal file
55
tests/unit/Exceptions/AuthenticationFailureTest.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Exceptions;
|
||||
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(AuthenticationFailure::class)]
|
||||
final class AuthenticationFailureTest extends TestCase
|
||||
{
|
||||
public function testAllowDenied(): void
|
||||
{
|
||||
$exception = AuthenticationFailure::deniedByAllowDenyRules();
|
||||
self::assertSame('allow-denied', $exception->failureType);
|
||||
self::assertSame('Access denied!', $exception->getMessage());
|
||||
}
|
||||
|
||||
public function testEmptyDenied(): void
|
||||
{
|
||||
$exception = AuthenticationFailure::emptyPasswordDeniedByConfiguration();
|
||||
self::assertSame('empty-denied', $exception->failureType);
|
||||
self::assertSame(
|
||||
'Login without a password is forbidden by configuration (see AllowNoPassword).',
|
||||
$exception->getMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testNoActivity(): void
|
||||
{
|
||||
$exception = AuthenticationFailure::loggedOutDueToInactivity();
|
||||
self::assertSame('no-activity', $exception->failureType);
|
||||
self::assertSame(
|
||||
'You have been automatically logged out due to inactivity of %s seconds.'
|
||||
. ' Once you log in again, you should be able to resume the work where you left off.',
|
||||
$exception->getMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testRootDenied(): void
|
||||
{
|
||||
$exception = AuthenticationFailure::rootDeniedByConfiguration();
|
||||
self::assertSame('root-denied', $exception->failureType);
|
||||
self::assertSame('Access denied!', $exception->getMessage());
|
||||
}
|
||||
|
||||
public function testServerDenied(): void
|
||||
{
|
||||
$exception = AuthenticationFailure::deniedByDatabaseServer();
|
||||
self::assertSame('server-denied', $exception->failureType);
|
||||
self::assertSame('Cannot log in to the database server.', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
@ -7,17 +7,15 @@ namespace PhpMyAdmin\Tests\Plugins\Auth;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Plugins\Auth\AuthenticationConfig;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Medium;
|
||||
use ReflectionProperty;
|
||||
use Throwable;
|
||||
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use function json_decode;
|
||||
|
||||
#[CoversClass(AuthenticationConfig::class)]
|
||||
#[Medium]
|
||||
@ -55,12 +53,25 @@ class AuthenticationConfigTest extends AbstractTestCase
|
||||
unset($this->object);
|
||||
}
|
||||
|
||||
public function testAuth(): void
|
||||
public function testShowLoginFormWithoutAjax(): void
|
||||
{
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(false);
|
||||
self::assertNull($this->object->showLoginForm());
|
||||
}
|
||||
|
||||
public function testShowLoginFormWithAjax(): void
|
||||
{
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(true);
|
||||
$this->expectException(ExitException::class);
|
||||
$this->object->showLoginForm();
|
||||
$response = $this->object->showLoginForm();
|
||||
self::assertNotNull($response);
|
||||
$body = (string) $response->getBody();
|
||||
self::assertJson($body);
|
||||
$json = json_decode($body, true);
|
||||
self::assertIsArray($json);
|
||||
self::assertArrayHasKey('reload_flag', $json);
|
||||
self::assertSame('1', $json['reload_flag']);
|
||||
}
|
||||
|
||||
public function testAuthCheck(): void
|
||||
@ -89,17 +100,9 @@ class AuthenticationConfigTest extends AbstractTestCase
|
||||
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
|
||||
$html = ob_get_clean();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
|
||||
self::assertIsString($html);
|
||||
$html = (string) $response->getBody();
|
||||
|
||||
self::assertStringContainsString(
|
||||
'You probably did not create a configuration file. You might want ' .
|
||||
|
||||
@ -4,10 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Plugins\Auth;
|
||||
|
||||
use Fig\Http\Message\StatusCodeInterface;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Error\ErrorHandler;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Plugins\Auth\AuthenticationCookie;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
@ -24,6 +25,7 @@ use Throwable;
|
||||
use function base64_decode;
|
||||
use function base64_encode;
|
||||
use function is_readable;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function mb_strlen;
|
||||
use function ob_get_clean;
|
||||
@ -76,37 +78,21 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
{
|
||||
$GLOBALS['conn_error'] = true;
|
||||
|
||||
$responseStub = new ResponseRendererStub();
|
||||
$responseStub->setAjax(true);
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
$responseRenderer = ResponseRenderer::getInstance();
|
||||
$responseRenderer->setAjax(true);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(200, $response->getStatusCode());
|
||||
self::assertFalse($responseStub->hasSuccessState());
|
||||
self::assertSame(['redirect_flag' => '1'], $responseStub->getJSONResult());
|
||||
}
|
||||
|
||||
private function getAuthErrorMockResponse(): void
|
||||
{
|
||||
// mock error handler
|
||||
|
||||
$mockErrorHandler = $this->getMockBuilder(ErrorHandler::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['hasDisplayErrors'])
|
||||
->getMock();
|
||||
|
||||
$mockErrorHandler->expects(self::once())
|
||||
->method('hasDisplayErrors')
|
||||
->with()
|
||||
->willReturn(true);
|
||||
|
||||
ErrorHandler::$instance = $mockErrorHandler;
|
||||
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
|
||||
$body = (string) $response->getBody();
|
||||
self::assertJson($body);
|
||||
$json = json_decode($body, true);
|
||||
self::assertIsArray($json);
|
||||
self::assertArrayHasKey('success', $json);
|
||||
self::assertFalse($json['success']);
|
||||
self::assertArrayHasKey('redirect_flag', $json);
|
||||
self::assertSame('1', $json['redirect_flag']);
|
||||
}
|
||||
|
||||
public function testAuthError(): void
|
||||
@ -132,17 +118,11 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
Current::$table = 'testTable';
|
||||
$config->settings['Servers'] = [1, 2];
|
||||
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
$result = $responseStub->getHTMLResult();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$result = (string) $response->getBody();
|
||||
|
||||
self::assertStringContainsString(' id="imLogo"', $result);
|
||||
|
||||
@ -202,14 +182,9 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
$result = $responseStub->getHTMLResult();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$result = (string) $response->getBody();
|
||||
|
||||
self::assertStringContainsString('id="imLogo"', $result);
|
||||
|
||||
@ -263,14 +238,9 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
$result = $responseStub->getHTMLResult();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$result = (string) $response->getBody();
|
||||
|
||||
self::assertStringContainsString('id="imLogo"', $result);
|
||||
|
||||
@ -569,18 +539,14 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
// mock for blowfish function
|
||||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['showFailure', 'cookieDecrypt'])
|
||||
->onlyMethods(['cookieDecrypt'])
|
||||
->getMock();
|
||||
|
||||
$this->object->expects(self::once())
|
||||
->method('cookieDecrypt')
|
||||
->willReturn('testBF');
|
||||
|
||||
$this->object->expects(self::once())
|
||||
->method('showFailure')
|
||||
->willThrowException(new ExitException());
|
||||
|
||||
$this->expectException(ExitException::class);
|
||||
$this->expectExceptionObject(AuthenticationFailure::loggedOutDueToInactivity());
|
||||
$this->object->readCredentials();
|
||||
}
|
||||
|
||||
@ -626,6 +592,7 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
$config->selectedServer['user'] = 'pmaUser';
|
||||
$config->settings['Servers'][1] = $arr;
|
||||
$config->settings['AllowArbitraryServer'] = true;
|
||||
$config->settings['PmaAbsoluteUri'] = 'http://localhost/phpmyadmin';
|
||||
$GLOBALS['pma_auth_server'] = 'b 2';
|
||||
$this->object->password = 'testPW';
|
||||
$config->settings['LoginCookieStore'] = 100;
|
||||
@ -635,8 +602,13 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
$this->object->storeCredentials();
|
||||
$this->expectException(ExitException::class);
|
||||
$this->object->rememberCredentials();
|
||||
$response = $this->object->rememberCredentials();
|
||||
self::assertNotNull($response);
|
||||
self::assertSame(StatusCodeInterface::STATUS_FOUND, $response->getStatusCode());
|
||||
self::assertStringEndsWith(
|
||||
'/phpmyadmin/index.php?route=/&db=db&table=table&lang=en',
|
||||
$response->getHeaderLine('Location'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testAuthFailsNoPass(): void
|
||||
@ -656,11 +628,11 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showFailure('empty-denied');
|
||||
$this->object->showFailure(AuthenticationFailure::emptyPasswordDeniedByConfiguration());
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
self::assertInstanceOf(ExitException::class, $throwable ?? null);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control'));
|
||||
self::assertSame(['no-cache'], $response->getHeader('Pragma'));
|
||||
@ -668,7 +640,7 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
|
||||
self::assertSame(
|
||||
$GLOBALS['conn_error'],
|
||||
'Login without a password is forbidden by configuration (see AllowNoPassword)',
|
||||
'Login without a password is forbidden by configuration (see AllowNoPassword).',
|
||||
);
|
||||
}
|
||||
|
||||
@ -724,11 +696,11 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showFailure('allow-denied');
|
||||
$this->object->showFailure(AuthenticationFailure::deniedByAllowDenyRules());
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
self::assertInstanceOf(ExitException::class, $throwable ?? null);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control'));
|
||||
self::assertSame(['no-cache'], $response->getHeader('Pragma'));
|
||||
@ -756,11 +728,11 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showFailure('no-activity');
|
||||
$this->object->showFailure(AuthenticationFailure::loggedOutDueToInactivity());
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
self::assertInstanceOf(ExitException::class, $throwable ?? null);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control'));
|
||||
self::assertSame(['no-cache'], $response->getHeader('Pragma'));
|
||||
@ -801,17 +773,17 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
$this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
self::assertInstanceOf(ExitException::class, $throwable ?? null);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control'));
|
||||
self::assertSame(['no-cache'], $response->getHeader('Pragma'));
|
||||
self::assertSame(200, $response->getStatusCode());
|
||||
|
||||
self::assertSame($GLOBALS['conn_error'], '#42 Cannot log in to the MySQL server');
|
||||
self::assertSame($GLOBALS['conn_error'], '#42 Cannot log in to the database server.');
|
||||
}
|
||||
|
||||
public function testAuthFailsErrno(): void
|
||||
@ -842,17 +814,17 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
$this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
self::assertInstanceOf(ExitException::class, $throwable ?? null);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control'));
|
||||
self::assertSame(['no-cache'], $response->getHeader('Pragma'));
|
||||
self::assertSame(200, $response->getStatusCode());
|
||||
|
||||
self::assertSame($GLOBALS['conn_error'], 'Cannot log in to the MySQL server');
|
||||
self::assertSame($GLOBALS['conn_error'], 'Cannot log in to the database server.');
|
||||
}
|
||||
|
||||
public function testGetEncryptionSecretEmpty(): void
|
||||
@ -954,9 +926,10 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
$_POST['pma_password'] = 'testPassword';
|
||||
|
||||
ob_start();
|
||||
$this->object->authenticate();
|
||||
$response = $this->object->authenticate();
|
||||
$result = ob_get_clean();
|
||||
|
||||
self::assertNull($response);
|
||||
/* Nothing should be printed */
|
||||
self::assertSame('', $result);
|
||||
|
||||
@ -999,31 +972,20 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
$config->selectedServer['AllowNoPassword'] = $nopass;
|
||||
$config->selectedServer['AllowDeny'] = $rules;
|
||||
|
||||
if ($expected !== '') {
|
||||
$this->getAuthErrorMockResponse();
|
||||
}
|
||||
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
$exception = null;
|
||||
try {
|
||||
$this->object->checkRules();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
$result = $responseStub->getHTMLResult();
|
||||
|
||||
if ($expected !== '') {
|
||||
self::assertInstanceOf(ExitException::class, $throwable ?? null);
|
||||
} catch (AuthenticationFailure $exception) {
|
||||
}
|
||||
|
||||
if ($expected === '') {
|
||||
self::assertSame($expected, $result);
|
||||
} else {
|
||||
self::assertStringContainsString($expected, $result);
|
||||
self::assertNull($exception, 'checkRules() should not throw an exception.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorHandler::$instance = null;
|
||||
self::assertInstanceOf(AuthenticationFailure::class, $exception);
|
||||
self::assertSame($expected, $exception->failureType);
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
@ -1031,9 +993,9 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
{
|
||||
return [
|
||||
'nopass-ok' => ['testUser', '', '1.2.3.4', true, true, [], ''],
|
||||
'nopass' => ['testUser', '', '1.2.3.4', true, false, [], 'Login without a password is forbidden'],
|
||||
'nopass' => ['testUser', '', '1.2.3.4', true, false, [], AuthenticationFailure::EMPTY_DENIED],
|
||||
'root-ok' => ['root', 'root', '1.2.3.4', true, true, [], ''],
|
||||
'root' => ['root', 'root', '1.2.3.4', false, true, [], 'Access denied!'],
|
||||
'root' => ['root', 'root', '1.2.3.4', false, true, [], AuthenticationFailure::ROOT_DENIED],
|
||||
'rules-deny-allow-ok' => [
|
||||
'root',
|
||||
'root',
|
||||
@ -1050,7 +1012,7 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
true,
|
||||
true,
|
||||
['order' => 'deny,allow', 'rules' => ['allow root 1.2.3.4', 'deny % from all']],
|
||||
'Access denied!',
|
||||
AuthenticationFailure::ALLOW_DENIED,
|
||||
],
|
||||
'rules-allow-deny-ok' => [
|
||||
'root',
|
||||
@ -1068,7 +1030,7 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
true,
|
||||
true,
|
||||
['order' => 'allow,deny', 'rules' => ['deny user from all', 'allow root 1.2.3.4']],
|
||||
'Access denied!',
|
||||
AuthenticationFailure::ALLOW_DENIED,
|
||||
],
|
||||
'rules-explicit-ok' => [
|
||||
'root',
|
||||
@ -1086,7 +1048,7 @@ class AuthenticationCookieTest extends AbstractTestCase
|
||||
true,
|
||||
true,
|
||||
['order' => 'explicit', 'rules' => ['deny user from all', 'allow root 1.2.3.4']],
|
||||
'Access denied!',
|
||||
AuthenticationFailure::ALLOW_DENIED,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ namespace PhpMyAdmin\Tests\Plugins\Auth;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Plugins\Auth\AuthenticationHttp;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
@ -16,11 +16,9 @@ use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Medium;
|
||||
use ReflectionProperty;
|
||||
use Throwable;
|
||||
|
||||
use function base64_encode;
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use function json_decode;
|
||||
|
||||
#[CoversClass(AuthenticationHttp::class)]
|
||||
#[Medium]
|
||||
@ -82,13 +80,8 @@ class AuthenticationHttpTest extends AbstractTestCase
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['Basic realm="phpMyAdmin verboseMessag"'], $response->getHeader('WWW-Authenticate'));
|
||||
self::assertSame(401, $response->getStatusCode());
|
||||
}
|
||||
@ -103,13 +96,8 @@ class AuthenticationHttpTest extends AbstractTestCase
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['Basic realm="phpMyAdmin hst"'], $response->getHeader('WWW-Authenticate'));
|
||||
self::assertSame(401, $response->getStatusCode());
|
||||
}
|
||||
@ -124,13 +112,8 @@ class AuthenticationHttpTest extends AbstractTestCase
|
||||
$responseStub = new ResponseRendererStub();
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
|
||||
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
$response = $this->object->showLoginForm();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
$response = $responseStub->getResponse();
|
||||
self::assertSame(['Basic realm="realmmessage"'], $response->getHeader('WWW-Authenticate'));
|
||||
self::assertSame(401, $response->getStatusCode());
|
||||
}
|
||||
@ -271,6 +254,8 @@ class AuthenticationHttpTest extends AbstractTestCase
|
||||
$config = Config::getInstance();
|
||||
$config->selectedServer['host'] = '';
|
||||
$_REQUEST = [];
|
||||
Current::$server = 0;
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(false);
|
||||
|
||||
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
||||
@ -284,40 +269,48 @@ class AuthenticationHttpTest extends AbstractTestCase
|
||||
DatabaseInterface::$instance = $dbi;
|
||||
$GLOBALS['errno'] = 31;
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(false);
|
||||
|
||||
$result = ob_get_clean();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
|
||||
self::assertIsString($result);
|
||||
$response = $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
|
||||
$result = (string) $response->getBody();
|
||||
self::assertStringContainsString('<p>error 123</p>', $result);
|
||||
|
||||
$this->object = $this->getMockBuilder(AuthenticationHttp::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['authForm'])
|
||||
->getMock();
|
||||
|
||||
$this->object->expects(self::exactly(2))
|
||||
->method('authForm')
|
||||
->willThrowException(new ExitException());
|
||||
// case 2
|
||||
$config->selectedServer['host'] = 'host';
|
||||
$GLOBALS['errno'] = 1045;
|
||||
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
} catch (ExitException) {
|
||||
}
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(false);
|
||||
|
||||
$response = $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
$result = (string) $response->getBody();
|
||||
self::assertStringContainsString('Wrong username/password. Access denied.', $result);
|
||||
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(false);
|
||||
|
||||
// case 3
|
||||
$GLOBALS['errno'] = 1043;
|
||||
$this->expectException(ExitException::class);
|
||||
$this->object->showFailure('');
|
||||
$response = $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
$result = (string) $response->getBody();
|
||||
self::assertStringContainsString('Wrong username/password. Access denied.', $result);
|
||||
}
|
||||
|
||||
public function testShowLoginFormWithAjax(): void
|
||||
{
|
||||
Current::$database = '';
|
||||
Current::$table = '';
|
||||
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
|
||||
ResponseRenderer::getInstance()->setAjax(true);
|
||||
$response = (new AuthenticationHttp())->showLoginForm();
|
||||
|
||||
$body = (string) $response->getBody();
|
||||
self::assertJson($body);
|
||||
$json = json_decode($body, true);
|
||||
self::assertIsArray($json);
|
||||
self::assertArrayHasKey('reload_flag', $json);
|
||||
self::assertSame('1', $json['reload_flag']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Config\Settings\Server;
|
||||
use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Plugins\Auth\AuthenticationSignon;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
@ -15,10 +16,7 @@ use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseRendererStub;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use ReflectionProperty;
|
||||
use Throwable;
|
||||
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use function session_get_cookie_params;
|
||||
use function session_id;
|
||||
use function session_name;
|
||||
@ -61,20 +59,8 @@ class AuthenticationSignonTest extends AbstractTestCase
|
||||
Config::getInstance()->selectedServer['SignonURL'] = '';
|
||||
$_REQUEST = [];
|
||||
ResponseRenderer::getInstance()->setAjax(false);
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
$this->object->showLoginForm();
|
||||
} catch (Throwable $throwable) {
|
||||
}
|
||||
|
||||
$result = ob_get_clean();
|
||||
|
||||
self::assertInstanceOf(ExitException::class, $throwable);
|
||||
|
||||
self::assertIsString($result);
|
||||
|
||||
self::assertStringContainsString('You must set SignonURL!', $result);
|
||||
$response = $this->object->showLoginForm();
|
||||
self::assertStringContainsString('You must set SignonURL!', (string) $response->getBody());
|
||||
}
|
||||
|
||||
public function testAuthLogoutURL(): void
|
||||
@ -260,12 +246,12 @@ class AuthenticationSignonTest extends AbstractTestCase
|
||||
->willThrowException(new ExitException());
|
||||
|
||||
try {
|
||||
$this->object->showFailure('empty-denied');
|
||||
$this->object->showFailure(AuthenticationFailure::emptyPasswordDeniedByConfiguration());
|
||||
} catch (ExitException) {
|
||||
}
|
||||
|
||||
self::assertSame(
|
||||
'Login without a password is forbidden by configuration (see AllowNoPassword)',
|
||||
'Login without a password is forbidden by configuration (see AllowNoPassword).',
|
||||
$_SESSION['PMA_single_signon_error_message'],
|
||||
);
|
||||
}
|
||||
@ -285,7 +271,7 @@ class AuthenticationSignonTest extends AbstractTestCase
|
||||
->willThrowException(new ExitException());
|
||||
|
||||
try {
|
||||
$this->object->showFailure('allow-denied');
|
||||
$this->object->showFailure(AuthenticationFailure::deniedByAllowDenyRules());
|
||||
} catch (ExitException) {
|
||||
}
|
||||
|
||||
@ -310,7 +296,7 @@ class AuthenticationSignonTest extends AbstractTestCase
|
||||
$config->settings['LoginCookieValidity'] = '1440';
|
||||
|
||||
try {
|
||||
$this->object->showFailure('no-activity');
|
||||
$this->object->showFailure(AuthenticationFailure::loggedOutDueToInactivity());
|
||||
} catch (ExitException) {
|
||||
}
|
||||
|
||||
@ -347,7 +333,7 @@ class AuthenticationSignonTest extends AbstractTestCase
|
||||
DatabaseInterface::$instance = $dbi;
|
||||
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
$this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
} catch (ExitException) {
|
||||
}
|
||||
|
||||
@ -380,11 +366,11 @@ class AuthenticationSignonTest extends AbstractTestCase
|
||||
DatabaseInterface::$instance = $dbi;
|
||||
|
||||
try {
|
||||
$this->object->showFailure('');
|
||||
$this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer());
|
||||
} catch (ExitException) {
|
||||
}
|
||||
|
||||
self::assertSame('Cannot log in to the MySQL server', $_SESSION['PMA_single_signon_error_message']);
|
||||
self::assertSame('Cannot log in to the database server.', $_SESSION['PMA_single_signon_error_message']);
|
||||
}
|
||||
|
||||
public function testSetCookieParamsDefaults(): void
|
||||
|
||||
@ -5,8 +5,10 @@ declare(strict_types=1);
|
||||
namespace PhpMyAdmin\Tests\Plugins;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Exceptions\AuthenticationFailure;
|
||||
use PhpMyAdmin\Exceptions\ExitException;
|
||||
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\Plugins\AuthenticationPlugin;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
@ -26,14 +28,20 @@ final class AuthenticationPluginTest extends AbstractTestCase
|
||||
DatabaseInterface::$instance = $dbi;
|
||||
|
||||
$object = new class extends AuthenticationPlugin {
|
||||
public function showLoginForm(): void
|
||||
public function showLoginForm(): Response|null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function readCredentials(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function showFailure(AuthenticationFailure $failure): Response
|
||||
{
|
||||
throw new ExitException();
|
||||
}
|
||||
};
|
||||
|
||||
$_SESSION['two_factor_check'] = false;
|
||||
@ -45,12 +53,9 @@ final class AuthenticationPluginTest extends AbstractTestCase
|
||||
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/');
|
||||
|
||||
$object->user = 'test_user';
|
||||
try {
|
||||
$object->checkTwoFactor($request);
|
||||
} catch (ExitException) {
|
||||
}
|
||||
$response = $object->checkTwoFactor($request);
|
||||
|
||||
$response = $responseRenderer->response();
|
||||
self::assertNotNull($response);
|
||||
self::assertStringContainsString(
|
||||
'You have enabled two factor authentication, please confirm your login.',
|
||||
(string) $response->getBody(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user