1415 lines
42 KiB
PHP
1415 lines
42 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace PhpMyAdmin\Tests\Plugins\Auth;
|
||
|
||
use PhpMyAdmin\DatabaseInterface;
|
||
use PhpMyAdmin\ErrorHandler;
|
||
use PhpMyAdmin\Footer;
|
||
use PhpMyAdmin\Header;
|
||
use PhpMyAdmin\Plugins\Auth\AuthenticationCookie;
|
||
use PhpMyAdmin\Tests\AbstractNetworkTestCase;
|
||
use ReflectionException;
|
||
use ReflectionMethod;
|
||
use function base64_encode;
|
||
use function function_exists;
|
||
use function is_readable;
|
||
use function json_encode;
|
||
use function ob_get_clean;
|
||
use function ob_start;
|
||
use function str_repeat;
|
||
use function str_shuffle;
|
||
use function strlen;
|
||
use function time;
|
||
|
||
class AuthenticationCookieTest extends AbstractNetworkTestCase
|
||
{
|
||
/** @var AuthenticationCookie */
|
||
protected $object;
|
||
|
||
/**
|
||
* Configures global environment.
|
||
*/
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
parent::defineVersionConstants();
|
||
parent::setLanguage();
|
||
parent::setTheme();
|
||
parent::setGlobalConfig();
|
||
$GLOBALS['PMA_Config']->enableBc();
|
||
$GLOBALS['server'] = 0;
|
||
$GLOBALS['text_dir'] = 'ltr';
|
||
$GLOBALS['db'] = 'db';
|
||
$GLOBALS['table'] = 'table';
|
||
$_POST['pma_password'] = '';
|
||
$this->object = new AuthenticationCookie();
|
||
$GLOBALS['PMA_PHP_SELF'] = '/phpmyadmin/';
|
||
$GLOBALS['cfg']['Server']['DisableIS'] = false;
|
||
}
|
||
|
||
/**
|
||
* tearDown for test cases
|
||
*/
|
||
protected function tearDown(): void
|
||
{
|
||
parent::tearDown();
|
||
unset($this->object);
|
||
}
|
||
|
||
/**
|
||
* @group medium
|
||
*/
|
||
public function testAuthErrorAJAX(): void
|
||
{
|
||
$mockResponse = $this->mockResponse();
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('isAjax')
|
||
->with()
|
||
->will($this->returnValue(true));
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('setRequestStatus')
|
||
->with(false);
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('addJSON')
|
||
->with(
|
||
'redirect_flag',
|
||
'1'
|
||
);
|
||
|
||
$GLOBALS['conn_error'] = true;
|
||
$this->assertTrue(
|
||
$this->object->showLoginForm()
|
||
);
|
||
}
|
||
|
||
private function getAuthErrorMockResponse(): void
|
||
{
|
||
$mockResponse = $this->mockResponse();
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('isAjax')
|
||
->with()
|
||
->will($this->returnValue(false));
|
||
|
||
// mock footer
|
||
$mockFooter = $this->getMockBuilder(Footer::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['setMinimal'])
|
||
->getMock();
|
||
|
||
$mockFooter->expects($this->once())
|
||
->method('setMinimal')
|
||
->with();
|
||
|
||
// mock header
|
||
|
||
$mockHeader = $this->getMockBuilder(Header::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(
|
||
[
|
||
'setBodyId',
|
||
'setTitle',
|
||
'disableMenuAndConsole',
|
||
'disableWarnings',
|
||
]
|
||
)
|
||
->getMock();
|
||
|
||
$mockHeader->expects($this->once())
|
||
->method('setBodyId')
|
||
->with('loginform');
|
||
|
||
$mockHeader->expects($this->once())
|
||
->method('setTitle')
|
||
->with('phpMyAdmin');
|
||
|
||
$mockHeader->expects($this->once())
|
||
->method('disableMenuAndConsole')
|
||
->with();
|
||
|
||
$mockHeader->expects($this->once())
|
||
->method('disableWarnings')
|
||
->with();
|
||
|
||
// set mocked headers and footers
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('getFooter')
|
||
->with()
|
||
->will($this->returnValue($mockFooter));
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('getHeader')
|
||
->with()
|
||
->will($this->returnValue($mockHeader));
|
||
|
||
$GLOBALS['cfg']['Servers'] = [
|
||
1,
|
||
2,
|
||
];
|
||
|
||
// mock error handler
|
||
|
||
$mockErrorHandler = $this->getMockBuilder(ErrorHandler::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['hasDisplayErrors'])
|
||
->getMock();
|
||
|
||
$mockErrorHandler->expects($this->once())
|
||
->method('hasDisplayErrors')
|
||
->with()
|
||
->will($this->returnValue(true));
|
||
|
||
$GLOBALS['error_handler'] = $mockErrorHandler;
|
||
}
|
||
|
||
/**
|
||
* @group medium
|
||
*/
|
||
public function testAuthError(): void
|
||
{
|
||
$_REQUEST['old_usr'] = '';
|
||
$GLOBALS['cfg']['LoginCookieRecall'] = true;
|
||
$GLOBALS['cfg']['blowfish_secret'] = 'secret';
|
||
$this->object->user = 'pmauser';
|
||
$GLOBALS['pma_auth_server'] = 'localhost';
|
||
|
||
$GLOBALS['conn_error'] = true;
|
||
$GLOBALS['cfg']['Lang'] = 'en';
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = true;
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$GLOBALS['db'] = 'testDb';
|
||
$GLOBALS['table'] = 'testTable';
|
||
$GLOBALS['cfg']['Servers'] = [1, 2];
|
||
$GLOBALS['error_handler'] = new ErrorHandler();
|
||
|
||
ob_start();
|
||
$this->object->showLoginForm();
|
||
$result = ob_get_clean();
|
||
|
||
$this->assertIsString($result);
|
||
|
||
$this->assertStringContainsString(
|
||
' id="imLogo"',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<div class="alert alert-danger" role="alert">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<form method="post" id="login_form" action="index.php?route=/" name="login_form" ' .
|
||
'class="disableAjax hide login js-show form-horizontal">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="text" name="pma_servername" id="input_servername" ' .
|
||
'value="localhost"',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="text" name="pma_username" id="input_username" ' .
|
||
'value="pmauser" size="24" class="textfield" autocomplete="username">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="password" name="pma_password" id="input_password" ' .
|
||
'value="" size="24" class="textfield" autocomplete="current-password">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<select name="server" id="select_server" ' .
|
||
'onchange="document.forms[\'login_form\'].' .
|
||
'elements[\'pma_servername\'].value = \'\'">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="hidden" name="db" value="testDb">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="hidden" name="table" value="testTable">',
|
||
$result
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @group medium
|
||
*/
|
||
public function testAuthCaptcha(): void
|
||
{
|
||
$mockResponse = $this->mockResponse();
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('isAjax')
|
||
->with()
|
||
->will($this->returnValue(false));
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('getFooter')
|
||
->with()
|
||
->will($this->returnValue(new Footer()));
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('getHeader')
|
||
->with()
|
||
->will($this->returnValue(new Header()));
|
||
|
||
$_REQUEST['old_usr'] = '';
|
||
$GLOBALS['cfg']['LoginCookieRecall'] = false;
|
||
|
||
$GLOBALS['cfg']['Lang'] = '';
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = false;
|
||
$GLOBALS['cfg']['Servers'] = [1];
|
||
$GLOBALS['cfg']['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = 'g-recaptcha';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = 'g-recaptcha-response';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = 'testprivkey';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = 'testpubkey';
|
||
$GLOBALS['server'] = 0;
|
||
|
||
$GLOBALS['error_handler'] = new ErrorHandler();
|
||
|
||
ob_start();
|
||
$this->object->showLoginForm();
|
||
$result = ob_get_clean();
|
||
|
||
$this->assertIsString($result);
|
||
|
||
$this->assertStringContainsString('id="imLogo"', $result);
|
||
|
||
// Check for language selection if locales are there
|
||
$loc = LOCALE_PATH . '/cs/LC_MESSAGES/phpmyadmin.mo';
|
||
if (is_readable($loc)) {
|
||
$this->assertStringContainsString(
|
||
'<select name="lang" class="autosubmit" lang="en" dir="ltr" ' .
|
||
'id="sel-lang">',
|
||
$result
|
||
);
|
||
}
|
||
|
||
$this->assertStringContainsString(
|
||
'<form method="post" id="login_form" action="index.php?route=/" name="login_form"' .
|
||
' class="disableAjax hide login js-show form-horizontal" autocomplete="off">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="hidden" name="server" value="0">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<script src="https://www.google.com/recaptcha/api.js?hl=en"'
|
||
. ' async defer></script>',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input class="btn btn-primary g-recaptcha" data-sitekey="testpubkey"'
|
||
. ' data-callback="Functions_recaptchaCallback" value="Go" type="submit" id="input_go">',
|
||
$result
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @group medium
|
||
*/
|
||
public function testAuthCaptchaCheckbox(): void
|
||
{
|
||
$mockResponse = $this->mockResponse();
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('isAjax')
|
||
->with()
|
||
->will($this->returnValue(false));
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('getFooter')
|
||
->with()
|
||
->will($this->returnValue(new Footer()));
|
||
|
||
$mockResponse->expects($this->once())
|
||
->method('getHeader')
|
||
->with()
|
||
->will($this->returnValue(new Header()));
|
||
|
||
$_REQUEST['old_usr'] = '';
|
||
$GLOBALS['cfg']['LoginCookieRecall'] = false;
|
||
|
||
$GLOBALS['cfg']['Lang'] = '';
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = false;
|
||
$GLOBALS['cfg']['Servers'] = [1];
|
||
$GLOBALS['cfg']['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = 'g-recaptcha';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = 'g-recaptcha-response';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = 'testprivkey';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = 'testpubkey';
|
||
$GLOBALS['cfg']['CaptchaMethod'] = 'checkbox';
|
||
$GLOBALS['server'] = 0;
|
||
|
||
$GLOBALS['error_handler'] = new ErrorHandler();
|
||
|
||
ob_start();
|
||
$this->object->showLoginForm();
|
||
$result = ob_get_clean();
|
||
|
||
$this->assertIsString($result);
|
||
|
||
$this->assertStringContainsString('id="imLogo"', $result);
|
||
|
||
// Check for language selection if locales are there
|
||
$loc = LOCALE_PATH . '/cs/LC_MESSAGES/phpmyadmin.mo';
|
||
if (is_readable($loc)) {
|
||
$this->assertStringContainsString(
|
||
'<select name="lang" class="autosubmit" lang="en" dir="ltr" ' .
|
||
'id="sel-lang">',
|
||
$result
|
||
);
|
||
}
|
||
|
||
$this->assertStringContainsString(
|
||
'<form method="post" id="login_form" action="index.php?route=/" name="login_form"' .
|
||
' class="disableAjax hide login js-show form-horizontal" autocomplete="off">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input type="hidden" name="server" value="0">',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<script src="https://www.google.com/recaptcha/api.js?hl=en"'
|
||
. ' async defer></script>',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<div class="g-recaptcha" data-sitekey="testpubkey"></div>',
|
||
$result
|
||
);
|
||
|
||
$this->assertStringContainsString(
|
||
'<input class="btn btn-primary" value="Go" type="submit" id="input_go">',
|
||
$result
|
||
);
|
||
}
|
||
|
||
public function testAuthHeader(): void
|
||
{
|
||
$GLOBALS['cfg']['LoginCookieDeleteAll'] = false;
|
||
$GLOBALS['cfg']['Servers'] = [1];
|
||
|
||
$this->mockResponse('Location: https://example.com/logout');
|
||
|
||
$GLOBALS['cfg']['Server']['LogoutURL'] = 'https://example.com/logout';
|
||
$GLOBALS['cfg']['Server']['auth_type'] = 'cookie';
|
||
|
||
$this->object->logOut();
|
||
}
|
||
|
||
public function testAuthHeaderPartial(): void
|
||
{
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
$GLOBALS['cfg']['LoginCookieDeleteAll'] = false;
|
||
$GLOBALS['cfg']['Servers'] = [
|
||
1,
|
||
2,
|
||
3,
|
||
];
|
||
$GLOBALS['cfg']['Server']['LogoutURL'] = 'https://example.com/logout';
|
||
$GLOBALS['cfg']['Server']['auth_type'] = 'cookie';
|
||
|
||
$_COOKIE['pmaAuth-2'] = '';
|
||
|
||
$this->mockResponse('Location: /phpmyadmin/index.php?route=/&server=2&lang=en');
|
||
|
||
$this->object->logOut();
|
||
}
|
||
|
||
public function testAuthCheckCaptcha(): void
|
||
{
|
||
$GLOBALS['cfg']['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = 'g-recaptcha';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = 'g-recaptcha-response';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = 'testprivkey';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = 'testpubkey';
|
||
$_POST['g-recaptcha-response'] = '';
|
||
$_POST['pma_username'] = 'testPMAUser';
|
||
|
||
$this->assertFalse(
|
||
$this->object->readCredentials()
|
||
);
|
||
|
||
$this->assertEquals(
|
||
'Missing reCAPTCHA verification, maybe it has been blocked by adblock?',
|
||
$GLOBALS['conn_error']
|
||
);
|
||
}
|
||
|
||
public function testLogoutDelete(): void
|
||
{
|
||
$this->mockResponse('Location: /phpmyadmin/index.php?route=/');
|
||
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$GLOBALS['cfg']['LoginCookieDeleteAll'] = true;
|
||
$GLOBALS['PMA_Config']->set('PmaAbsoluteUri', '');
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
$GLOBALS['cfg']['Servers'] = [1];
|
||
|
||
$_COOKIE['pmaAuth-0'] = 'test';
|
||
|
||
$this->object->logOut();
|
||
|
||
$this->assertArrayNotHasKey(
|
||
'pmaAuth-0',
|
||
$_COOKIE
|
||
);
|
||
}
|
||
|
||
public function testLogout(): void
|
||
{
|
||
$this->mockResponse('Location: /phpmyadmin/index.php?route=/');
|
||
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$GLOBALS['cfg']['LoginCookieDeleteAll'] = false;
|
||
$GLOBALS['PMA_Config']->set('PmaAbsoluteUri', '');
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
$GLOBALS['cfg']['Servers'] = [1];
|
||
$GLOBALS['server'] = 1;
|
||
$GLOBALS['cfg']['Server'] = ['auth_type' => 'cookie'];
|
||
|
||
$_COOKIE['pmaAuth-1'] = 'test';
|
||
|
||
$this->object->logOut();
|
||
|
||
$this->assertArrayNotHasKey(
|
||
'pmaAuth-1',
|
||
$_COOKIE
|
||
);
|
||
}
|
||
|
||
public function testAuthCheckArbitrary(): void
|
||
{
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$_REQUEST['old_usr'] = '';
|
||
$_POST['pma_username'] = 'testPMAUser';
|
||
$_REQUEST['pma_servername'] = 'testPMAServer';
|
||
$_POST['pma_password'] = 'testPMAPSWD';
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = true;
|
||
|
||
$this->assertTrue(
|
||
$this->object->readCredentials()
|
||
);
|
||
|
||
$this->assertEquals(
|
||
'testPMAUser',
|
||
$this->object->user
|
||
);
|
||
|
||
$this->assertEquals(
|
||
'testPMAPSWD',
|
||
$this->object->password
|
||
);
|
||
|
||
$this->assertEquals(
|
||
'testPMAServer',
|
||
$GLOBALS['pma_auth_server']
|
||
);
|
||
|
||
$this->assertArrayNotHasKey(
|
||
'pmaAuth-1',
|
||
$_COOKIE
|
||
);
|
||
}
|
||
|
||
public function testAuthCheckInvalidCookie(): void
|
||
{
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = true;
|
||
$_REQUEST['pma_servername'] = 'testPMAServer';
|
||
$_POST['pma_password'] = 'testPMAPSWD';
|
||
$_POST['pma_username'] = '';
|
||
$GLOBALS['server'] = 1;
|
||
$_COOKIE['pmaUser-1'] = '';
|
||
$_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09');
|
||
|
||
$this->assertFalse(
|
||
$this->object->readCredentials()
|
||
);
|
||
}
|
||
|
||
public function testAuthCheckExpires(): void
|
||
{
|
||
$GLOBALS['server'] = 1;
|
||
$_COOKIE['pmaServer-1'] = 'pmaServ1';
|
||
$_COOKIE['pmaUser-1'] = 'pmaUser1';
|
||
$_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09');
|
||
$_COOKIE['pmaAuth-1'] = '';
|
||
$GLOBALS['cfg']['blowfish_secret'] = 'secret';
|
||
$_SESSION['last_access_time'] = time() - 1000;
|
||
$GLOBALS['cfg']['LoginCookieValidity'] = 1440;
|
||
|
||
$this->assertFalse(
|
||
$this->object->readCredentials()
|
||
);
|
||
}
|
||
|
||
public function testAuthCheckDecryptUser(): void
|
||
{
|
||
$GLOBALS['server'] = 1;
|
||
$_REQUEST['old_usr'] = '';
|
||
$_POST['pma_username'] = '';
|
||
$_COOKIE['pmaServer-1'] = 'pmaServ1';
|
||
$_COOKIE['pmaUser-1'] = 'pmaUser1';
|
||
$_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09');
|
||
$GLOBALS['cfg']['blowfish_secret'] = 'secret';
|
||
$_SESSION['last_access_time'] = '';
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
|
||
// mock for blowfish function
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['cookieDecrypt'])
|
||
->getMock();
|
||
|
||
$this->object->expects($this->once())
|
||
->method('cookieDecrypt')
|
||
->will($this->returnValue('testBF'));
|
||
|
||
$this->assertFalse(
|
||
$this->object->readCredentials()
|
||
);
|
||
|
||
$this->assertEquals(
|
||
'testBF',
|
||
$this->object->user
|
||
);
|
||
}
|
||
|
||
public function testAuthCheckDecryptPassword(): void
|
||
{
|
||
$GLOBALS['server'] = 1;
|
||
$_REQUEST['old_usr'] = '';
|
||
$_POST['pma_username'] = '';
|
||
$_COOKIE['pmaServer-1'] = 'pmaServ1';
|
||
$_COOKIE['pmaUser-1'] = 'pmaUser1';
|
||
$_COOKIE['pmaAuth-1'] = 'pmaAuth1';
|
||
$_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09');
|
||
$GLOBALS['cfg']['blowfish_secret'] = 'secret';
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$_SESSION['browser_access_time']['default'] = time() - 1000;
|
||
$GLOBALS['cfg']['LoginCookieValidity'] = 1440;
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
|
||
// mock for blowfish function
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['cookieDecrypt'])
|
||
->getMock();
|
||
|
||
$this->object->expects($this->at(1))
|
||
->method('cookieDecrypt')
|
||
->will($this->returnValue('{"password":""}'));
|
||
|
||
$this->assertTrue(
|
||
$this->object->readCredentials()
|
||
);
|
||
|
||
$this->assertTrue(
|
||
$GLOBALS['from_cookie']
|
||
);
|
||
|
||
$this->assertEquals(
|
||
'',
|
||
$this->object->password
|
||
);
|
||
}
|
||
|
||
public function testAuthCheckAuthFails(): void
|
||
{
|
||
$GLOBALS['server'] = 1;
|
||
$_REQUEST['old_usr'] = '';
|
||
$_POST['pma_username'] = '';
|
||
$_COOKIE['pmaServer-1'] = 'pmaServ1';
|
||
$_COOKIE['pmaUser-1'] = 'pmaUser1';
|
||
$_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09');
|
||
$GLOBALS['cfg']['blowfish_secret'] = 'secret';
|
||
$_SESSION['last_access_time'] = 1;
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$GLOBALS['cfg']['LoginCookieValidity'] = 0;
|
||
$_SESSION['browser_access_time']['default'] = -1;
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
|
||
// mock for blowfish function
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['showFailure', 'cookieDecrypt'])
|
||
->getMock();
|
||
|
||
$this->object->expects($this->once())
|
||
->method('cookieDecrypt')
|
||
->will($this->returnValue('testBF'));
|
||
|
||
$this->object->expects($this->once())
|
||
->method('showFailure');
|
||
|
||
$this->assertFalse(
|
||
$this->object->readCredentials()
|
||
);
|
||
}
|
||
|
||
public function testAuthSetUser(): void
|
||
{
|
||
$this->object->user = 'pmaUser2';
|
||
$arr = [
|
||
'host' => 'a',
|
||
'port' => 1,
|
||
'socket' => true,
|
||
'ssl' => true,
|
||
'user' => 'pmaUser2',
|
||
];
|
||
|
||
$GLOBALS['cfg']['Server'] = $arr;
|
||
$GLOBALS['cfg']['Server']['user'] = 'pmaUser';
|
||
$GLOBALS['cfg']['Servers'][1] = $arr;
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = true;
|
||
$GLOBALS['pma_auth_server'] = 'b 2';
|
||
$this->object->password = 'testPW';
|
||
$GLOBALS['server'] = 2;
|
||
$GLOBALS['cfg']['LoginCookieStore'] = true;
|
||
$GLOBALS['from_cookie'] = true;
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
|
||
$this->object->storeCredentials();
|
||
|
||
$this->object->rememberCredentials();
|
||
|
||
$this->assertArrayHasKey(
|
||
'pmaUser-2',
|
||
$_COOKIE
|
||
);
|
||
|
||
$this->assertArrayHasKey(
|
||
'pmaAuth-2',
|
||
$_COOKIE
|
||
);
|
||
|
||
$arr['password'] = 'testPW';
|
||
$arr['host'] = 'b';
|
||
$arr['port'] = '2';
|
||
$this->assertEquals(
|
||
$arr,
|
||
$GLOBALS['cfg']['Server']
|
||
);
|
||
}
|
||
|
||
public function testAuthSetUserWithHeaders(): void
|
||
{
|
||
$this->object->user = 'pmaUser2';
|
||
$arr = [
|
||
'host' => 'a',
|
||
'port' => 1,
|
||
'socket' => true,
|
||
'ssl' => true,
|
||
'user' => 'pmaUser2',
|
||
];
|
||
|
||
$GLOBALS['cfg']['Server'] = $arr;
|
||
$GLOBALS['cfg']['Server']['host'] = 'b';
|
||
$GLOBALS['cfg']['Server']['user'] = 'pmaUser';
|
||
$GLOBALS['cfg']['Servers'][1] = $arr;
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = true;
|
||
$GLOBALS['pma_auth_server'] = 'b 2';
|
||
$this->object->password = 'testPW';
|
||
$GLOBALS['server'] = 2;
|
||
$GLOBALS['cfg']['LoginCookieStore'] = true;
|
||
$GLOBALS['from_cookie'] = false;
|
||
|
||
$this->mockResponse(
|
||
$this->stringContains('&server=2&lang=en')
|
||
);
|
||
|
||
$this->object->storeCredentials();
|
||
$this->object->rememberCredentials();
|
||
}
|
||
|
||
public function testAuthFailsNoPass(): void
|
||
{
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['showLoginForm'])
|
||
->getMock();
|
||
|
||
$GLOBALS['server'] = 2;
|
||
$_COOKIE['pmaAuth-2'] = 'pass';
|
||
|
||
$this->mockResponse(
|
||
['Cache-Control: no-store, no-cache, must-revalidate'],
|
||
['Pragma: no-cache']
|
||
);
|
||
$this->object->showFailure('empty-denied');
|
||
|
||
$this->assertEquals(
|
||
$GLOBALS['conn_error'],
|
||
'Login without a password is forbidden by configuration'
|
||
. ' (see AllowNoPassword)'
|
||
);
|
||
}
|
||
|
||
public function dataProviderPasswordLength(): array
|
||
{
|
||
return [
|
||
[
|
||
str_repeat('a', 1000),
|
||
false,
|
||
'Your password is too long. To prevent denial-of-service attacks,'
|
||
. ' phpMyAdmin restricts passwords to less than 1000 characters.',
|
||
],
|
||
[
|
||
str_repeat('a', 1001),
|
||
false,
|
||
'Your password is too long. To prevent denial-of-service attacks,'
|
||
. ' phpMyAdmin restricts passwords to less than 1000 characters.',
|
||
],
|
||
[
|
||
str_repeat('a', 3000),
|
||
false,
|
||
'Your password is too long. To prevent denial-of-service attacks,'
|
||
. ' phpMyAdmin restricts passwords to less than 1000 characters.',
|
||
],
|
||
[
|
||
str_repeat('a', 256),
|
||
true,
|
||
null,
|
||
],
|
||
[
|
||
'',
|
||
true,
|
||
null,
|
||
],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* @dataProvider dataProviderPasswordLength
|
||
*/
|
||
public function testAuthFailsTooLongPass(string $password, bool $trueFalse, ?string $connError): void
|
||
{
|
||
$_POST['pma_username'] = str_shuffle('123456987rootfoobar');
|
||
$_POST['pma_password'] = $password;
|
||
|
||
if ($trueFalse === false) {
|
||
$this->assertFalse(
|
||
$this->object->readCredentials()
|
||
);
|
||
} else {
|
||
$this->assertTrue(
|
||
$this->object->readCredentials()
|
||
);
|
||
}
|
||
|
||
$this->assertEquals(
|
||
$GLOBALS['conn_error'],
|
||
$connError
|
||
);
|
||
}
|
||
|
||
public function testAuthFailsDeny(): void
|
||
{
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['showLoginForm'])
|
||
->getMock();
|
||
|
||
$GLOBALS['server'] = 2;
|
||
$_COOKIE['pmaAuth-2'] = 'pass';
|
||
|
||
$this->mockResponse(
|
||
['Cache-Control: no-store, no-cache, must-revalidate'],
|
||
['Pragma: no-cache']
|
||
);
|
||
$this->object->showFailure('allow-denied');
|
||
|
||
$this->assertEquals(
|
||
$GLOBALS['conn_error'],
|
||
'Access denied!'
|
||
);
|
||
}
|
||
|
||
public function testAuthFailsActivity(): void
|
||
{
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['showLoginForm'])
|
||
->getMock();
|
||
|
||
$GLOBALS['server'] = 2;
|
||
$_COOKIE['pmaAuth-2'] = 'pass';
|
||
|
||
$GLOBALS['allowDeny_forbidden'] = '';
|
||
$GLOBALS['cfg']['LoginCookieValidity'] = 10;
|
||
|
||
$this->mockResponse(
|
||
['Cache-Control: no-store, no-cache, must-revalidate'],
|
||
['Pragma: no-cache']
|
||
);
|
||
$this->object->showFailure('no-activity');
|
||
|
||
$this->assertEquals(
|
||
$GLOBALS['conn_error'],
|
||
'You have been automatically logged out due to inactivity of 10 seconds.'
|
||
. ' Once you log in again, you should be able to resume the work where you left off.'
|
||
);
|
||
}
|
||
|
||
public function testAuthFailsDBI(): void
|
||
{
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['showLoginForm'])
|
||
->getMock();
|
||
|
||
$GLOBALS['server'] = 2;
|
||
$_COOKIE['pmaAuth-2'] = 'pass';
|
||
|
||
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
||
->disableOriginalConstructor()
|
||
->getMock();
|
||
|
||
$dbi->expects($this->at(0))
|
||
->method('getError')
|
||
->will($this->returnValue(false));
|
||
|
||
$GLOBALS['dbi'] = $dbi;
|
||
$GLOBALS['errno'] = 42;
|
||
|
||
$this->mockResponse(
|
||
['Cache-Control: no-store, no-cache, must-revalidate'],
|
||
['Pragma: no-cache']
|
||
);
|
||
$this->object->showFailure('');
|
||
|
||
$this->assertEquals(
|
||
$GLOBALS['conn_error'],
|
||
'#42 Cannot log in to the MySQL server'
|
||
);
|
||
}
|
||
|
||
public function testAuthFailsErrno(): void
|
||
{
|
||
$this->object = $this->getMockBuilder(AuthenticationCookie::class)
|
||
->disableOriginalConstructor()
|
||
->setMethods(['showLoginForm'])
|
||
->getMock();
|
||
|
||
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
||
->disableOriginalConstructor()
|
||
->getMock();
|
||
|
||
$dbi->expects($this->at(0))
|
||
->method('getError')
|
||
->will($this->returnValue(false));
|
||
|
||
$GLOBALS['dbi'] = $dbi;
|
||
$GLOBALS['server'] = 2;
|
||
$_COOKIE['pmaAuth-2'] = 'pass';
|
||
|
||
unset($GLOBALS['errno']);
|
||
|
||
$this->mockResponse(
|
||
['Cache-Control: no-store, no-cache, must-revalidate'],
|
||
['Pragma: no-cache']
|
||
);
|
||
$this->object->showFailure('');
|
||
|
||
$this->assertEquals(
|
||
$GLOBALS['conn_error'],
|
||
'Cannot log in to the MySQL server'
|
||
);
|
||
}
|
||
|
||
public function testGetEncryptionSecretEmpty(): void
|
||
{
|
||
$method = new ReflectionMethod(
|
||
AuthenticationCookie::class,
|
||
'getEncryptionSecret'
|
||
);
|
||
$method->setAccessible(true);
|
||
|
||
$GLOBALS['cfg']['blowfish_secret'] = '';
|
||
$_SESSION['encryption_key'] = '';
|
||
|
||
$result = $method->invoke($this->object, null);
|
||
|
||
$this->assertEquals(
|
||
$result,
|
||
$_SESSION['encryption_key']
|
||
);
|
||
|
||
$this->assertEquals(
|
||
32,
|
||
strlen($result)
|
||
);
|
||
}
|
||
|
||
public function testGetEncryptionSecretConfigured(): void
|
||
{
|
||
$method = new ReflectionMethod(
|
||
AuthenticationCookie::class,
|
||
'getEncryptionSecret'
|
||
);
|
||
$method->setAccessible(true);
|
||
|
||
$GLOBALS['cfg']['blowfish_secret'] = 'notEmpty';
|
||
|
||
$result = $method->invoke($this->object, null);
|
||
|
||
$this->assertEquals(
|
||
'notEmpty',
|
||
$result
|
||
);
|
||
}
|
||
|
||
public function testCookieEncrypt(): void
|
||
{
|
||
$this->object->setIV('testiv09testiv09');
|
||
// works with the openssl extension active or inactive
|
||
$this->assertEquals(
|
||
'{"iv":"dGVzdGl2MDl0ZXN0aXYwOQ==","mac":"347aa45ae1ade00c980f31129ec2def'
|
||
. 'ef18b2bfd","payload":"YDEaxOfP9nD9q\/2pC6hjfQ=="}',
|
||
$this->object->cookieEncrypt('data123', 'sec321')
|
||
);
|
||
}
|
||
|
||
public function testCookieEncryptPHPSecLib(): void
|
||
{
|
||
$this->object->setUseOpenSSL(false);
|
||
$this->testCookieEncrypt();
|
||
}
|
||
|
||
public function testCookieEncryptOpenSSL(): void
|
||
{
|
||
if (! function_exists('openssl_encrypt')) {
|
||
$this->markTestSkipped('openssl not available');
|
||
}
|
||
$this->object->setUseOpenSSL(true);
|
||
$this->testCookieEncrypt();
|
||
}
|
||
|
||
public function testCookieDecrypt(): void
|
||
{
|
||
// works with the openssl extension active or inactive
|
||
$this->assertEquals(
|
||
'data123',
|
||
$this->object->cookieDecrypt(
|
||
'{"iv":"dGVzdGl2MDl0ZXN0aXYwOQ==","mac":"347aa45ae1ade00c980f31129ec'
|
||
. '2defef18b2bfd","payload":"YDEaxOfP9nD9q\/2pC6hjfQ=="}',
|
||
'sec321'
|
||
)
|
||
);
|
||
$this->assertEquals(
|
||
'root',
|
||
$this->object->cookieDecrypt(
|
||
'{"iv":"AclJhCM7ryNiuPnw3Y8cXg==","mac":"d0ef75e852bc162e81496e116dc'
|
||
. '571182cb2cba6","payload":"O4vrt9R1xyzAw7ypvrLmQA=="}',
|
||
':Kb1?)c(r{]-{`HW*hOzuufloK(M~!p'
|
||
)
|
||
);
|
||
$this->assertFalse(
|
||
$this->object->cookieDecrypt(
|
||
'{"iv":"AclJhCM7ryNiuPnw3Y8cXg==","mac":"d0ef75e852bc162e81496e116dc'
|
||
. '571182cb2cba6","payload":"O4vrt9R1xyzAw7ypvrLmQA=="}',
|
||
'aedzoiefpzf,zf1z7ef6ef84'
|
||
)
|
||
);
|
||
}
|
||
|
||
public function testCookieDecryptPHPSecLib(): void
|
||
{
|
||
$this->object->setUseOpenSSL(false);
|
||
$this->testCookieDecrypt();
|
||
}
|
||
|
||
public function testCookieDecryptOpenSSL(): void
|
||
{
|
||
if (! function_exists('openssl_encrypt')) {
|
||
$this->markTestSkipped('openssl not available');
|
||
}
|
||
$this->object->setUseOpenSSL(true);
|
||
$this->testCookieDecrypt();
|
||
}
|
||
|
||
public function testCookieDecryptInvalid(): void
|
||
{
|
||
// works with the openssl extension active or inactive
|
||
$this->assertFalse(
|
||
$this->object->cookieDecrypt(
|
||
'{"iv":0,"mac":0,"payload":0}',
|
||
'sec321'
|
||
)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Test for secret splitting using getAESSecret
|
||
*
|
||
* @param string $secret secret
|
||
* @param string $mac mac
|
||
* @param string $aes aes
|
||
*
|
||
* @dataProvider secretsProvider
|
||
*/
|
||
public function testMACSecretSplit(string $secret, string $mac, string $aes): void
|
||
{
|
||
$this->assertNotEmpty($aes);// Useless check
|
||
$this->assertEquals(
|
||
$mac,
|
||
$this->object->getMACSecret($secret)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Test for secret splitting using getMACSecret and getAESSecret
|
||
*
|
||
* @param string $secret secret
|
||
* @param string $mac mac
|
||
* @param string $aes aes
|
||
*
|
||
* @dataProvider secretsProvider
|
||
*/
|
||
public function testAESSecretSplit(string $secret, string $mac, string $aes): void
|
||
{
|
||
$this->assertNotEmpty($mac);// Useless check
|
||
$this->assertEquals(
|
||
$aes,
|
||
$this->object->getAESSecret($secret)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @throws ReflectionException
|
||
*/
|
||
public function testPasswordChange(): void
|
||
{
|
||
$newPassword = 'PMAPASSWD2';
|
||
$GLOBALS['PMA_Config']->set('is_https', false);
|
||
$GLOBALS['cfg']['AllowArbitraryServer'] = true;
|
||
$GLOBALS['pma_auth_server'] = 'b 2';
|
||
$_SESSION['encryption_key'] = '';
|
||
$this->object->setIV('testiv09testiv09');
|
||
|
||
$this->object->handlePasswordChange($newPassword);
|
||
|
||
$payload = [
|
||
'password' => $newPassword,
|
||
'server' => 'b 2',
|
||
];
|
||
$method = new ReflectionMethod(
|
||
AuthenticationCookie::class,
|
||
'getSessionEncryptionSecret'
|
||
);
|
||
$method->setAccessible(true);
|
||
|
||
$encryptedCookie = $this->object->cookieEncrypt(
|
||
(string) json_encode($payload),
|
||
$method->invoke($this->object, null)
|
||
);
|
||
$this->assertEquals(
|
||
$_COOKIE['pmaAuth-' . $GLOBALS['server']],
|
||
$encryptedCookie
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Data provider for secrets splitting.
|
||
*
|
||
* @return array
|
||
*/
|
||
public function secretsProvider(): array
|
||
{
|
||
return [
|
||
// Optimal case
|
||
[
|
||
'1234567890123456abcdefghijklmnop',
|
||
'1234567890123456',
|
||
'abcdefghijklmnop',
|
||
],
|
||
// Overlapping secret
|
||
[
|
||
'12345678901234567',
|
||
'1234567890123456',
|
||
'2345678901234567',
|
||
],
|
||
// Short secret
|
||
[
|
||
'1234567890123456',
|
||
'1234567890123451',
|
||
'2345678901234562',
|
||
],
|
||
// Really short secret
|
||
[
|
||
'12',
|
||
'1111111111111111',
|
||
'2222222222222222',
|
||
],
|
||
// Too short secret
|
||
[
|
||
'1',
|
||
'1111111111111111',
|
||
'1111111111111111',
|
||
],
|
||
];
|
||
}
|
||
|
||
public function testAuthenticate(): void
|
||
{
|
||
$GLOBALS['cfg']['CaptchaApi'] = '';
|
||
$GLOBALS['cfg']['CaptchaRequestParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaResponseParam'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPrivateKey'] = '';
|
||
$GLOBALS['cfg']['CaptchaLoginPublicKey'] = '';
|
||
$GLOBALS['cfg']['Server']['AllowRoot'] = false;
|
||
$GLOBALS['cfg']['Server']['AllowNoPassword'] = false;
|
||
$_REQUEST['old_usr'] = '';
|
||
$_POST['pma_username'] = 'testUser';
|
||
$_POST['pma_password'] = 'testPassword';
|
||
|
||
ob_start();
|
||
$this->object->authenticate();
|
||
$result = ob_get_clean();
|
||
|
||
/* Nothing should be printed */
|
||
$this->assertEquals('', $result);
|
||
|
||
/* Verify readCredentials worked */
|
||
$this->assertEquals('testUser', $this->object->user);
|
||
$this->assertEquals('testPassword', $this->object->password);
|
||
|
||
/* Verify storeCredentials worked */
|
||
$this->assertEquals('testUser', $GLOBALS['cfg']['Server']['user']);
|
||
$this->assertEquals('testPassword', $GLOBALS['cfg']['Server']['password']);
|
||
}
|
||
|
||
/**
|
||
* @param string $user user
|
||
* @param string $pass pass
|
||
* @param string $ip ip
|
||
* @param bool $root root
|
||
* @param bool $nopass nopass
|
||
* @param array $rules rules
|
||
* @param string $expected expected result
|
||
*
|
||
* @dataProvider checkRulesProvider
|
||
*/
|
||
public function testCheckRules(
|
||
string $user,
|
||
string $pass,
|
||
string $ip,
|
||
bool $root,
|
||
bool $nopass,
|
||
array $rules,
|
||
string $expected
|
||
): void {
|
||
$this->object->user = $user;
|
||
$this->object->password = $pass;
|
||
$this->object->storeCredentials();
|
||
|
||
$_SERVER['REMOTE_ADDR'] = $ip;
|
||
|
||
$GLOBALS['cfg']['Server']['AllowRoot'] = $root;
|
||
$GLOBALS['cfg']['Server']['AllowNoPassword'] = $nopass;
|
||
$GLOBALS['cfg']['Server']['AllowDeny'] = $rules;
|
||
|
||
if (! empty($expected)) {
|
||
$this->getAuthErrorMockResponse();
|
||
}
|
||
|
||
ob_start();
|
||
$this->object->checkRules();
|
||
$result = ob_get_clean();
|
||
|
||
$this->assertIsString($result);
|
||
|
||
if (empty($expected)) {
|
||
$this->assertEquals($expected, $result);
|
||
} else {
|
||
$this->assertStringContainsString($expected, $result);
|
||
}
|
||
}
|
||
|
||
public function checkRulesProvider(): array
|
||
{
|
||
return [
|
||
'nopass-ok' => [
|
||
'testUser',
|
||
'',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[],
|
||
'',
|
||
],
|
||
'nopass' => [
|
||
'testUser',
|
||
'',
|
||
'1.2.3.4',
|
||
true,
|
||
false,
|
||
[],
|
||
'Login without a password is forbidden',
|
||
],
|
||
'root-ok' => [
|
||
'root',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[],
|
||
'',
|
||
],
|
||
'root' => [
|
||
'root',
|
||
'root',
|
||
'1.2.3.4',
|
||
false,
|
||
true,
|
||
[],
|
||
'Access denied!',
|
||
],
|
||
'rules-deny-allow-ok' => [
|
||
'root',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[
|
||
'order' => 'deny,allow',
|
||
'rules' => [
|
||
'allow root 1.2.3.4',
|
||
'deny % from all',
|
||
],
|
||
],
|
||
'',
|
||
],
|
||
'rules-deny-allow-reject' => [
|
||
'user',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[
|
||
'order' => 'deny,allow',
|
||
'rules' => [
|
||
'allow root 1.2.3.4',
|
||
'deny % from all',
|
||
],
|
||
],
|
||
'Access denied!',
|
||
],
|
||
'rules-allow-deny-ok' => [
|
||
'root',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[
|
||
'order' => 'allow,deny',
|
||
'rules' => [
|
||
'deny user from all',
|
||
'allow root 1.2.3.4',
|
||
],
|
||
],
|
||
'',
|
||
],
|
||
'rules-allow-deny-reject' => [
|
||
'user',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[
|
||
'order' => 'allow,deny',
|
||
'rules' => [
|
||
'deny user from all',
|
||
'allow root 1.2.3.4',
|
||
],
|
||
],
|
||
'Access denied!',
|
||
],
|
||
'rules-explicit-ok' => [
|
||
'root',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[
|
||
'order' => 'explicit',
|
||
'rules' => [
|
||
'deny user from all',
|
||
'allow root 1.2.3.4',
|
||
],
|
||
],
|
||
'',
|
||
],
|
||
'rules-explicit-reject' => [
|
||
'user',
|
||
'root',
|
||
'1.2.3.4',
|
||
true,
|
||
true,
|
||
[
|
||
'order' => 'explicit',
|
||
'rules' => [
|
||
'deny user from all',
|
||
'allow root 1.2.3.4',
|
||
],
|
||
],
|
||
'Access denied!',
|
||
],
|
||
];
|
||
}
|
||
}
|