phpmyadmin/tests/unit/Plugins/Auth/AuthenticationSignonTest.php
Kamil Tekiela abdaefca82 Remove PreserveGlobalState
Signed-off-by: Kamil Tekiela <tekiela246@gmail.com>
2024-03-21 15:17:30 +01:00

414 lines
13 KiB
PHP

<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Plugins\Auth;
use PhpMyAdmin\Config;
use PhpMyAdmin\Config\Settings\Server;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Exceptions\ExitException;
use PhpMyAdmin\Plugins\Auth\AuthenticationSignon;
use PhpMyAdmin\ResponseRenderer;
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;
#[CoversClass(AuthenticationSignon::class)]
class AuthenticationSignonTest extends AbstractTestCase
{
protected AuthenticationSignon $object;
/**
* Configures global environment.
*/
protected function setUp(): void
{
parent::setUp();
$this->setLanguage();
$this->setGlobalConfig();
DatabaseInterface::$instance = $this->createDatabaseInterface();
Current::$database = 'db';
Current::$table = 'table';
$this->object = new AuthenticationSignon();
}
/**
* tearDown for test cases
*/
protected function tearDown(): void
{
parent::tearDown();
unset($this->object);
}
public function testAuth(): void
{
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
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);
}
public function testAuthLogoutURL(): void
{
$responseStub = new ResponseRendererStub();
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
$config = Config::getInstance();
$config->selectedServer['SignonURL'] = 'https://example.com/SignonURL';
$config->selectedServer['LogoutURL'] = 'https://example.com/logoutURL';
$this->object->logOut();
$response = $responseStub->getResponse();
self::assertSame(['https://example.com/logoutURL'], $response->getHeader('Location'));
self::assertSame(302, $response->getStatusCode());
}
public function testAuthLogout(): void
{
$responseStub = new ResponseRendererStub();
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
$GLOBALS['header'] = [];
$config = Config::getInstance();
$config->selectedServer['SignonURL'] = 'https://example.com/SignonURL';
$config->selectedServer['LogoutURL'] = '';
$this->object->logOut();
$response = $responseStub->getResponse();
self::assertSame(['https://example.com/SignonURL'], $response->getHeader('Location'));
self::assertSame(302, $response->getStatusCode());
}
public function testAuthCheckEmpty(): void
{
Config::getInstance()->selectedServer['SignonURL'] = 'https://example.com/SignonURL';
$_SESSION['LAST_SIGNON_URL'] = 'https://example.com/SignonDiffURL';
self::assertFalse(
$this->object->readCredentials(),
);
}
public function testAuthCheckSession(): void
{
$config = Config::getInstance();
$config->selectedServer['SignonURL'] = 'https://example.com/SignonURL';
$_SESSION['LAST_SIGNON_URL'] = 'https://example.com/SignonURL';
$config->selectedServer['SignonScript'] = './examples/signon-script.php';
$config->selectedServer['SignonSession'] = 'session123';
$config->selectedServer['SignonCookieParams'] = [];
$config->selectedServer['host'] = 'localhost';
$config->selectedServer['port'] = '80';
$config->selectedServer['user'] = 'user';
self::assertTrue(
$this->object->readCredentials(),
);
self::assertSame('user', $this->object->user);
self::assertSame('password', $this->object->password);
self::assertSame('https://example.com/SignonURL', $_SESSION['LAST_SIGNON_URL']);
}
public function testAuthCheckToken(): void
{
$_SESSION = [' PMA_token ' => 'eefefef'];
$responseStub = new ResponseRendererStub();
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub);
$config = Config::getInstance();
$config->selectedServer = (new Server([
'SignonURL' => 'https://example.com/SignonURL',
'SignonSession' => 'session123',
'SignonCookieParams' => [],
'host' => 'localhost',
'port' => '80',
'user' => 'user',
'SignonScript' => '',
]))->asArray();
$_COOKIE['session123'] = true;
$_SESSION['PMA_single_signon_user'] = 'user123';
$_SESSION['PMA_single_signon_password'] = 'pass123';
$_SESSION['PMA_single_signon_host'] = 'local';
$_SESSION['PMA_single_signon_port'] = '12';
$_SESSION['PMA_single_signon_cfgupdate'] = ['foo' => 'bar'];
$_SESSION['PMA_single_signon_token'] = 'pmaToken';
$sessionName = session_name();
$sessionID = session_id();
$this->object->logOut();
$response = $responseStub->getResponse();
self::assertSame(['https://example.com/SignonURL'], $response->getHeader('Location'));
self::assertSame(302, $response->getStatusCode());
self::assertSame(
(new Server([
'SignonURL' => 'https://example.com/SignonURL',
'SignonScript' => '',
'SignonSession' => 'session123',
'SignonCookieParams' => [],
'host' => 'localhost',
'port' => '80',
'user' => 'user',
]))->asArray(),
$config->selectedServer,
);
self::assertSame(
$sessionName,
session_name(),
);
self::assertSame(
$sessionID,
session_id(),
);
self::assertArrayNotHasKey('LAST_SIGNON_URL', $_SESSION);
}
public function testAuthCheckKeep(): void
{
$config = Config::getInstance();
$config->selectedServer['SignonURL'] = 'https://example.com/SignonURL';
$config->selectedServer['SignonSession'] = 'session123';
$config->selectedServer['SignonCookieParams'] = [];
$config->selectedServer['host'] = 'localhost';
$config->selectedServer['port'] = '80';
$config->selectedServer['user'] = 'user';
$config->selectedServer['SignonScript'] = '';
$_COOKIE['session123'] = true;
$_REQUEST['old_usr'] = '';
$_SESSION['PMA_single_signon_user'] = 'user123';
$_SESSION['PMA_single_signon_password'] = 'pass123';
$_SESSION['PMA_single_signon_host'] = 'local';
$_SESSION['PMA_single_signon_port'] = '12';
$_SESSION['PMA_single_signon_cfgupdate'] = ['foo' => 'bar'];
$_SESSION['PMA_single_signon_token'] = 'pmaToken';
self::assertTrue(
$this->object->readCredentials(),
);
self::assertSame('user123', $this->object->user);
self::assertSame('pass123', $this->object->password);
}
public function testAuthSetUser(): void
{
$this->object->user = 'testUser123';
$this->object->password = 'testPass123';
self::assertTrue(
$this->object->storeCredentials(),
);
$config = Config::getInstance();
self::assertSame('testUser123', $config->selectedServer['user']);
self::assertSame('testPass123', $config->selectedServer['password']);
}
public function testAuthFailsForbidden(): void
{
Config::getInstance()->selectedServer['SignonSession'] = 'newSession';
$_COOKIE['newSession'] = '42';
$this->object = $this->getMockBuilder(AuthenticationSignon::class)
->disableOriginalConstructor()
->onlyMethods(['showLoginForm'])
->getMock();
$this->object->expects(self::exactly(1))
->method('showLoginForm')
->willThrowException(new ExitException());
try {
$this->object->showFailure('empty-denied');
} catch (ExitException) {
}
self::assertSame(
'Login without a password is forbidden by configuration (see AllowNoPassword)',
$_SESSION['PMA_single_signon_error_message'],
);
}
public function testAuthFailsDeny(): void
{
Config::getInstance()->selectedServer['SignonSession'] = 'newSession';
$_COOKIE['newSession'] = '42';
$this->object = $this->getMockBuilder(AuthenticationSignon::class)
->disableOriginalConstructor()
->onlyMethods(['showLoginForm'])
->getMock();
$this->object->expects(self::exactly(1))
->method('showLoginForm')
->willThrowException(new ExitException());
try {
$this->object->showFailure('allow-denied');
} catch (ExitException) {
}
self::assertSame('Access denied!', $_SESSION['PMA_single_signon_error_message']);
}
public function testAuthFailsTimeout(): void
{
$config = Config::getInstance();
$config->selectedServer['SignonSession'] = 'newSession';
$_COOKIE['newSession'] = '42';
$this->object = $this->getMockBuilder(AuthenticationSignon::class)
->disableOriginalConstructor()
->onlyMethods(['showLoginForm'])
->getMock();
$this->object->expects(self::exactly(1))
->method('showLoginForm')
->willThrowException(new ExitException());
$config->settings['LoginCookieValidity'] = '1440';
try {
$this->object->showFailure('no-activity');
} catch (ExitException) {
}
self::assertSame(
'You have been automatically logged out due to inactivity of'
. ' 1440 seconds. Once you log in again, you should be able to'
. ' resume the work where you left off.',
$_SESSION['PMA_single_signon_error_message'],
);
}
public function testAuthFailsMySQLError(): void
{
Config::getInstance()->selectedServer['SignonSession'] = 'newSession';
$_COOKIE['newSession'] = '42';
$this->object = $this->getMockBuilder(AuthenticationSignon::class)
->disableOriginalConstructor()
->onlyMethods(['showLoginForm'])
->getMock();
$this->object->expects(self::exactly(1))
->method('showLoginForm')
->willThrowException(new ExitException());
$dbi = $this->getMockBuilder(DatabaseInterface::class)
->disableOriginalConstructor()
->getMock();
$dbi->expects(self::once())
->method('getError')
->willReturn('error<123>');
DatabaseInterface::$instance = $dbi;
try {
$this->object->showFailure('');
} catch (ExitException) {
}
self::assertSame('error&lt;123&gt;', $_SESSION['PMA_single_signon_error_message']);
}
public function testAuthFailsConnect(): void
{
Config::getInstance()->selectedServer['SignonSession'] = 'newSession';
$_COOKIE['newSession'] = '42';
unset($GLOBALS['errno']);
$this->object = $this->getMockBuilder(AuthenticationSignon::class)
->disableOriginalConstructor()
->onlyMethods(['showLoginForm'])
->getMock();
$this->object->expects(self::exactly(1))
->method('showLoginForm')
->willThrowException(new ExitException());
$dbi = $this->getMockBuilder(DatabaseInterface::class)
->disableOriginalConstructor()
->getMock();
$dbi->expects(self::once())
->method('getError')
->willReturn('');
DatabaseInterface::$instance = $dbi;
try {
$this->object->showFailure('');
} catch (ExitException) {
}
self::assertSame('Cannot log in to the MySQL server', $_SESSION['PMA_single_signon_error_message']);
}
public function testSetCookieParamsDefaults(): void
{
$this->object = $this->getMockBuilder(AuthenticationSignon::class)
->disableOriginalConstructor()
->onlyMethods(['setCookieParams'])
->getMock();
$this->object->setCookieParams([]);
$defaultOptions = [
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => false,
'samesite' => '',
];
self::assertSame(
$defaultOptions,
session_get_cookie_params(),
);
}
}