1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace PhpMyAdmin\Tests\Plugins\Auth; |
||
6 | |||
7 | use Fig\Http\Message\StatusCodeInterface; |
||
8 | use PhpMyAdmin\Config; |
||
9 | use PhpMyAdmin\Current; |
||
10 | use PhpMyAdmin\Dbal\DatabaseInterface; |
||
11 | use PhpMyAdmin\Exceptions\AuthenticationFailure; |
||
12 | use PhpMyAdmin\Exceptions\ExitException; |
||
13 | use PhpMyAdmin\Plugins\Auth\AuthenticationCookie; |
||
14 | use PhpMyAdmin\ResponseRenderer; |
||
15 | use PhpMyAdmin\Tests\AbstractTestCase; |
||
16 | use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseRendererStub; |
||
17 | use PHPUnit\Framework\Attributes\CoversClass; |
||
18 | use PHPUnit\Framework\Attributes\DataProvider; |
||
19 | use PHPUnit\Framework\Attributes\Medium; |
||
20 | use ReflectionException; |
||
21 | use ReflectionMethod; |
||
22 | use ReflectionProperty; |
||
23 | use Throwable; |
||
24 | |||
25 | use function base64_decode; |
||
26 | use function base64_encode; |
||
27 | use function is_readable; |
||
28 | use function json_decode; |
||
29 | use function json_encode; |
||
30 | use function mb_strlen; |
||
31 | use function ob_get_clean; |
||
32 | use function ob_start; |
||
33 | use function random_bytes; |
||
34 | use function str_repeat; |
||
35 | use function str_shuffle; |
||
36 | use function time; |
||
37 | |||
38 | use const SODIUM_CRYPTO_SECRETBOX_KEYBYTES; |
||
39 | |||
40 | #[CoversClass(AuthenticationCookie::class)] |
||
41 | #[Medium] |
||
42 | class AuthenticationCookieTest extends AbstractTestCase |
||
43 | { |
||
44 | protected AuthenticationCookie $object; |
||
45 | |||
46 | /** |
||
47 | * Configures global environment. |
||
48 | */ |
||
49 | protected function setUp(): void |
||
50 | { |
||
51 | parent::setUp(); |
||
52 | |||
53 | $this->setLanguage(); |
||
54 | |||
55 | $this->setGlobalConfig(); |
||
56 | |||
57 | DatabaseInterface::$instance = $this->createDatabaseInterface(); |
||
58 | Current::$database = 'db'; |
||
59 | Current::$table = 'table'; |
||
60 | $_POST['pma_password'] = ''; |
||
61 | $this->object = new AuthenticationCookie(); |
||
62 | $_SERVER['PHP_SELF'] = '/phpmyadmin/index.php'; |
||
63 | Config::getInstance()->selectedServer['DisableIS'] = false; |
||
64 | AuthenticationCookie::$connectionError = ''; |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * tearDown for test cases |
||
69 | */ |
||
70 | protected function tearDown(): void |
||
71 | { |
||
72 | parent::tearDown(); |
||
73 | |||
74 | unset($this->object); |
||
75 | } |
||
76 | |||
77 | public function testAuthErrorAJAX(): void |
||
78 | { |
||
79 | AuthenticationCookie::$connectionError = 'Error'; |
||
80 | |||
81 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null); |
||
82 | $responseRenderer = ResponseRenderer::getInstance(); |
||
83 | $responseRenderer->setAjax(true); |
||
84 | |||
85 | $response = $this->object->showLoginForm(); |
||
86 | |||
87 | self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode()); |
||
88 | $body = (string) $response->getBody(); |
||
89 | self::assertJson($body); |
||
90 | $json = json_decode($body, true); |
||
91 | self::assertIsArray($json); |
||
92 | self::assertArrayHasKey('success', $json); |
||
93 | self::assertFalse($json['success']); |
||
94 | self::assertArrayHasKey('redirect_flag', $json); |
||
95 | self::assertSame('1', $json['redirect_flag']); |
||
96 | } |
||
97 | |||
98 | public function testAuthError(): void |
||
99 | { |
||
100 | $_REQUEST = []; |
||
101 | |||
102 | $_REQUEST['old_usr'] = ''; |
||
103 | $config = Config::getInstance(); |
||
104 | $config->settings['LoginCookieRecall'] = true; |
||
105 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
106 | $this->object->user = 'pmauser'; |
||
107 | AuthenticationCookie::$authServer = 'localhost'; |
||
108 | |||
109 | AuthenticationCookie::$connectionError = 'Error'; |
||
110 | $config->set('Lang', 'en'); |
||
111 | $config->settings['AllowArbitraryServer'] = true; |
||
112 | $config->settings['CaptchaApi'] = ''; |
||
113 | $config->settings['CaptchaRequestParam'] = ''; |
||
114 | $config->settings['CaptchaResponseParam'] = ''; |
||
115 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
116 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
117 | Current::$database = 'testDb'; |
||
118 | Current::$table = 'testTable'; |
||
119 | $config->settings['Servers'] = [1, 2]; |
||
120 | |||
121 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null); |
||
122 | |||
123 | $response = $this->object->showLoginForm(); |
||
124 | |||
125 | $result = (string) $response->getBody(); |
||
126 | |||
127 | self::assertStringContainsString(' id="imLogo"', $result); |
||
128 | |||
129 | self::assertStringContainsString('<div class="alert alert-danger" role="alert">', $result); |
||
130 | |||
131 | self::assertStringContainsString( |
||
132 | '<form method="post" id="login_form" action="index.php?route=/" name="login_form" ' . |
||
133 | 'class="disableAjax hide js-show">', |
||
134 | $result, |
||
135 | ); |
||
136 | |||
137 | self::assertStringContainsString( |
||
138 | '<input type="text" name="pma_servername" id="serverNameInput" value="localhost"', |
||
139 | $result, |
||
140 | ); |
||
141 | |||
142 | self::assertStringContainsString( |
||
143 | '<input type="text" name="pma_username" id="input_username" ' . |
||
144 | 'value="pmauser" class="form-control" autocomplete="username" spellcheck="false" autofocus>', |
||
145 | $result, |
||
146 | ); |
||
147 | |||
148 | self::assertStringContainsString( |
||
149 | '<input type="password" name="pma_password" id="input_password" ' . |
||
150 | 'value="" class="form-control" autocomplete="current-password" spellcheck="false">', |
||
151 | $result, |
||
152 | ); |
||
153 | |||
154 | self::assertStringContainsString( |
||
155 | '<select name="server" id="select_server" class="form-select" ' . |
||
156 | 'onchange="document.forms[\'login_form\'].' . |
||
157 | 'elements[\'pma_servername\'].value = \'\'">', |
||
158 | $result, |
||
159 | ); |
||
160 | |||
161 | self::assertStringContainsString('<input type="hidden" name="db" value="testDb">', $result); |
||
162 | |||
163 | self::assertStringContainsString('<input type="hidden" name="table" value="testTable">', $result); |
||
164 | } |
||
165 | |||
166 | public function testAuthCaptcha(): void |
||
167 | { |
||
168 | $_REQUEST['old_usr'] = ''; |
||
169 | $config = Config::getInstance(); |
||
170 | $config->settings['LoginCookieRecall'] = false; |
||
171 | |||
172 | $config->set('Lang', ''); |
||
173 | $config->settings['AllowArbitraryServer'] = false; |
||
174 | $config->settings['Servers'] = [1]; |
||
175 | $config->settings['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js'; |
||
176 | $config->settings['CaptchaRequestParam'] = 'g-recaptcha'; |
||
177 | $config->settings['CaptchaResponseParam'] = 'g-recaptcha-response'; |
||
178 | $config->settings['CaptchaLoginPrivateKey'] = 'testprivkey'; |
||
179 | $config->settings['CaptchaLoginPublicKey'] = 'testpubkey'; |
||
180 | Current::$server = 2; |
||
181 | |||
182 | $responseStub = new ResponseRendererStub(); |
||
183 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
184 | |||
185 | $response = $this->object->showLoginForm(); |
||
186 | |||
187 | $result = (string) $response->getBody(); |
||
188 | |||
189 | self::assertStringContainsString('id="imLogo"', $result); |
||
190 | |||
191 | // Check for language selection if locales are there |
||
192 | $loc = LOCALE_PATH . '/cs/LC_MESSAGES/phpmyadmin.mo'; |
||
193 | if (is_readable($loc)) { |
||
194 | self::assertStringContainsString( |
||
195 | '<select name="lang" class="form-select autosubmit" lang="en" dir="ltr"' |
||
196 | . ' id="languageSelect" aria-labelledby="languageSelectLabel">', |
||
197 | $result, |
||
198 | ); |
||
199 | } |
||
200 | |||
201 | self::assertStringContainsString( |
||
202 | '<form method="post" id="login_form" action="index.php?route=/" name="login_form"' . |
||
203 | ' class="disableAjax hide js-show" autocomplete="off">', |
||
204 | $result, |
||
205 | ); |
||
206 | |||
207 | self::assertStringContainsString('<input type="hidden" name="server" value="2">', $result); |
||
208 | |||
209 | self::assertStringContainsString( |
||
210 | '<script src="https://www.google.com/recaptcha/api.js?hl=en" async defer></script>', |
||
211 | $result, |
||
212 | ); |
||
213 | |||
214 | self::assertStringContainsString( |
||
215 | '<input class="btn btn-primary g-recaptcha" data-sitekey="testpubkey"' |
||
216 | . ' data-callback="recaptchaCallback" value="Log in" type="submit" id="input_go">', |
||
217 | $result, |
||
218 | ); |
||
219 | } |
||
220 | |||
221 | public function testAuthCaptchaCheckbox(): void |
||
222 | { |
||
223 | $_REQUEST['old_usr'] = ''; |
||
224 | $config = Config::getInstance(); |
||
225 | $config->settings['LoginCookieRecall'] = false; |
||
226 | |||
227 | $config->set('Lang', ''); |
||
228 | $config->settings['AllowArbitraryServer'] = false; |
||
229 | $config->settings['Servers'] = [1]; |
||
230 | $config->settings['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js'; |
||
231 | $config->settings['CaptchaRequestParam'] = 'g-recaptcha'; |
||
232 | $config->settings['CaptchaResponseParam'] = 'g-recaptcha-response'; |
||
233 | $config->settings['CaptchaLoginPrivateKey'] = 'testprivkey'; |
||
234 | $config->settings['CaptchaLoginPublicKey'] = 'testpubkey'; |
||
235 | $config->settings['CaptchaMethod'] = 'checkbox'; |
||
236 | Current::$server = 2; |
||
237 | |||
238 | $responseStub = new ResponseRendererStub(); |
||
239 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
240 | |||
241 | $response = $this->object->showLoginForm(); |
||
242 | |||
243 | $result = (string) $response->getBody(); |
||
244 | |||
245 | self::assertStringContainsString('id="imLogo"', $result); |
||
246 | |||
247 | // Check for language selection if locales are there |
||
248 | $loc = LOCALE_PATH . '/cs/LC_MESSAGES/phpmyadmin.mo'; |
||
249 | if (is_readable($loc)) { |
||
250 | self::assertStringContainsString( |
||
251 | '<select name="lang" class="form-select autosubmit" lang="en" dir="ltr"' |
||
252 | . ' id="languageSelect" aria-labelledby="languageSelectLabel">', |
||
253 | $result, |
||
254 | ); |
||
255 | } |
||
256 | |||
257 | self::assertStringContainsString( |
||
258 | '<form method="post" id="login_form" action="index.php?route=/" name="login_form"' . |
||
259 | ' class="disableAjax hide js-show" autocomplete="off">', |
||
260 | $result, |
||
261 | ); |
||
262 | |||
263 | self::assertStringContainsString('<input type="hidden" name="server" value="2">', $result); |
||
264 | |||
265 | self::assertStringContainsString( |
||
266 | '<script src="https://www.google.com/recaptcha/api.js?hl=en" async defer></script>', |
||
267 | $result, |
||
268 | ); |
||
269 | |||
270 | self::assertStringContainsString('<div class="g-recaptcha" data-sitekey="testpubkey"></div>', $result); |
||
271 | |||
272 | self::assertStringContainsString( |
||
273 | '<input class="btn btn-primary" value="Log in" type="submit" id="input_go">', |
||
274 | $result, |
||
275 | ); |
||
276 | } |
||
277 | |||
278 | public function testAuthHeader(): void |
||
279 | { |
||
280 | $config = Config::getInstance(); |
||
281 | $config->settings['LoginCookieDeleteAll'] = false; |
||
282 | $config->settings['Servers'] = [1]; |
||
283 | |||
284 | $responseStub = new ResponseRendererStub(); |
||
285 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
286 | |||
287 | $config->selectedServer['LogoutURL'] = 'https://example.com/logout'; |
||
288 | $config->selectedServer['auth_type'] = 'cookie'; |
||
289 | |||
290 | $this->object->logOut(); |
||
291 | |||
292 | $response = $responseStub->getResponse(); |
||
293 | self::assertSame(['https://example.com/logout'], $response->getHeader('Location')); |
||
294 | self::assertSame(302, $response->getStatusCode()); |
||
295 | } |
||
296 | |||
297 | public function testAuthHeaderPartial(): void |
||
298 | { |
||
299 | $config = Config::getInstance(); |
||
300 | $config->settings['LoginCookieDeleteAll'] = false; |
||
301 | $config->settings['Servers'] = [1, 2, 3]; |
||
302 | $config->selectedServer['LogoutURL'] = 'https://example.com/logout'; |
||
303 | $config->selectedServer['auth_type'] = 'cookie'; |
||
304 | |||
305 | $_COOKIE['pmaAuth-2'] = ''; |
||
306 | |||
307 | $responseStub = new ResponseRendererStub(); |
||
308 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
309 | |||
310 | $this->object->logOut(); |
||
311 | |||
312 | $response = $responseStub->getResponse(); |
||
313 | self::assertSame(['/phpmyadmin/index.php?route=/&server=2&lang=en'], $response->getHeader('Location')); |
||
314 | self::assertSame(302, $response->getStatusCode()); |
||
315 | } |
||
316 | |||
317 | public function testAuthCheckCaptcha(): void |
||
318 | { |
||
319 | $config = Config::getInstance(); |
||
320 | $config->settings['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js'; |
||
321 | $config->settings['CaptchaRequestParam'] = 'g-recaptcha'; |
||
322 | $config->settings['CaptchaResponseParam'] = 'g-recaptcha-response'; |
||
323 | $config->settings['CaptchaLoginPrivateKey'] = 'testprivkey'; |
||
324 | $config->settings['CaptchaLoginPublicKey'] = 'testpubkey'; |
||
325 | $_POST['g-recaptcha-response'] = ''; |
||
326 | $_POST['pma_username'] = 'testPMAUser'; |
||
327 | |||
328 | self::assertFalse( |
||
329 | $this->object->readCredentials(), |
||
330 | ); |
||
331 | |||
332 | self::assertSame( |
||
333 | 'Missing Captcha verification, maybe it has been blocked by adblock?', |
||
334 | AuthenticationCookie::$connectionError, |
||
335 | ); |
||
336 | } |
||
337 | |||
338 | public function testLogoutDelete(): void |
||
339 | { |
||
340 | $responseStub = new ResponseRendererStub(); |
||
341 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
342 | |||
343 | $config = Config::getInstance(); |
||
344 | $config->settings['CaptchaApi'] = ''; |
||
345 | $config->settings['CaptchaRequestParam'] = ''; |
||
346 | $config->settings['CaptchaResponseParam'] = ''; |
||
347 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
348 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
349 | $config->settings['LoginCookieDeleteAll'] = true; |
||
350 | $config->set('PmaAbsoluteUri', ''); |
||
351 | $config->settings['Servers'] = [1]; |
||
352 | |||
353 | $_COOKIE['pmaAuth-0'] = 'test'; |
||
354 | |||
355 | $this->object->logOut(); |
||
356 | |||
357 | $response = $responseStub->getResponse(); |
||
358 | self::assertSame(['/phpmyadmin/index.php?route=/'], $response->getHeader('Location')); |
||
359 | self::assertSame(302, $response->getStatusCode()); |
||
360 | |||
361 | self::assertArrayNotHasKey('pmaAuth-0', $_COOKIE); |
||
362 | } |
||
363 | |||
364 | public function testLogout(): void |
||
365 | { |
||
366 | $responseStub = new ResponseRendererStub(); |
||
367 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
368 | |||
369 | $config = Config::getInstance(); |
||
370 | $config->settings['CaptchaApi'] = ''; |
||
371 | $config->settings['CaptchaRequestParam'] = ''; |
||
372 | $config->settings['CaptchaResponseParam'] = ''; |
||
373 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
374 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
375 | $config->settings['LoginCookieDeleteAll'] = false; |
||
376 | $config->set('PmaAbsoluteUri', ''); |
||
377 | $config->settings['Servers'] = [1]; |
||
378 | $config->selectedServer = ['auth_type' => 'cookie']; |
||
379 | |||
380 | $_COOKIE['pmaAuth-1'] = 'test'; |
||
381 | |||
382 | $this->object->logOut(); |
||
383 | |||
384 | $response = $responseStub->getResponse(); |
||
385 | self::assertSame(['/phpmyadmin/index.php?route=/'], $response->getHeader('Location')); |
||
386 | self::assertSame(302, $response->getStatusCode()); |
||
387 | |||
388 | self::assertArrayNotHasKey('pmaAuth-1', $_COOKIE); |
||
389 | } |
||
390 | |||
391 | public function testAuthCheckArbitrary(): void |
||
392 | { |
||
393 | $config = Config::getInstance(); |
||
394 | $config->settings['CaptchaApi'] = ''; |
||
395 | $config->settings['CaptchaRequestParam'] = ''; |
||
396 | $config->settings['CaptchaResponseParam'] = ''; |
||
397 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
398 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
399 | $_REQUEST['old_usr'] = ''; |
||
400 | $_POST['pma_username'] = 'testPMAUser'; |
||
401 | $_REQUEST['pma_servername'] = 'testPMAServer'; |
||
402 | $_POST['pma_password'] = 'testPMAPSWD'; |
||
403 | $config->settings['AllowArbitraryServer'] = true; |
||
404 | |||
405 | self::assertTrue( |
||
406 | $this->object->readCredentials(), |
||
407 | ); |
||
408 | |||
409 | self::assertSame('testPMAUser', $this->object->user); |
||
410 | |||
411 | self::assertSame('testPMAPSWD', $this->object->password); |
||
412 | |||
413 | self::assertSame('testPMAServer', AuthenticationCookie::$authServer); |
||
414 | |||
415 | self::assertArrayNotHasKey('pmaAuth-1', $_COOKIE); |
||
416 | } |
||
417 | |||
418 | public function testAuthCheckInvalidCookie(): void |
||
419 | { |
||
420 | Config::getInstance()->settings['AllowArbitraryServer'] = true; |
||
421 | $_REQUEST['pma_servername'] = 'testPMAServer'; |
||
422 | $_POST['pma_password'] = 'testPMAPSWD'; |
||
423 | $_POST['pma_username'] = ''; |
||
424 | $_COOKIE['pmaUser-1'] = ''; |
||
425 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
426 | |||
427 | self::assertFalse( |
||
428 | $this->object->readCredentials(), |
||
429 | ); |
||
430 | } |
||
431 | |||
432 | public function testAuthCheckExpires(): void |
||
433 | { |
||
434 | $_COOKIE['pmaServer-1'] = 'pmaServ1'; |
||
435 | $_COOKIE['pmaUser-1'] = 'pmaUser1'; |
||
436 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
437 | $_COOKIE['pmaAuth-1'] = ''; |
||
438 | $config = Config::getInstance(); |
||
439 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
440 | $_SESSION['last_access_time'] = time() - 1000; |
||
441 | $config->settings['LoginCookieValidity'] = 1440; |
||
442 | |||
443 | self::assertFalse( |
||
444 | $this->object->readCredentials(), |
||
445 | ); |
||
446 | } |
||
447 | |||
448 | public function testAuthCheckDecryptUser(): void |
||
449 | { |
||
450 | $_REQUEST['old_usr'] = ''; |
||
451 | $_POST['pma_username'] = ''; |
||
452 | $_COOKIE['pmaServer-1'] = 'pmaServ1'; |
||
453 | $_COOKIE['pmaUser-1'] = 'pmaUser1'; |
||
454 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
455 | $config = Config::getInstance(); |
||
456 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
457 | $_SESSION['last_access_time'] = ''; |
||
458 | $config->settings['CaptchaApi'] = ''; |
||
459 | $config->settings['CaptchaRequestParam'] = ''; |
||
460 | $config->settings['CaptchaResponseParam'] = ''; |
||
461 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
462 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
463 | |||
464 | // mock for blowfish function |
||
465 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
466 | ->disableOriginalConstructor() |
||
467 | ->onlyMethods(['cookieDecrypt']) |
||
468 | ->getMock(); |
||
469 | |||
470 | $this->object->expects(self::once()) |
||
471 | ->method('cookieDecrypt') |
||
472 | ->willReturn('testBF'); |
||
473 | |||
474 | self::assertFalse( |
||
475 | $this->object->readCredentials(), |
||
476 | ); |
||
477 | |||
478 | self::assertSame('testBF', $this->object->user); |
||
479 | } |
||
480 | |||
481 | public function testAuthCheckDecryptPassword(): void |
||
482 | { |
||
483 | $_REQUEST['old_usr'] = ''; |
||
484 | $_POST['pma_username'] = ''; |
||
485 | $_COOKIE['pmaServer-1'] = 'pmaServ1'; |
||
486 | $_COOKIE['pmaUser-1'] = 'pmaUser1'; |
||
487 | $_COOKIE['pmaAuth-1'] = 'pmaAuth1'; |
||
488 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
489 | $config = Config::getInstance(); |
||
490 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
491 | $config->settings['CaptchaApi'] = ''; |
||
492 | $config->settings['CaptchaRequestParam'] = ''; |
||
493 | $config->settings['CaptchaResponseParam'] = ''; |
||
494 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
495 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
496 | $_SESSION['browser_access_time']['default'] = time() - 1000; |
||
497 | $config->settings['LoginCookieValidity'] = 1440; |
||
498 | |||
499 | // mock for blowfish function |
||
500 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
501 | ->disableOriginalConstructor() |
||
502 | ->onlyMethods(['cookieDecrypt']) |
||
503 | ->getMock(); |
||
504 | |||
505 | $this->object->expects(self::exactly(2)) |
||
506 | ->method('cookieDecrypt') |
||
507 | ->willReturn('{"password":""}'); |
||
508 | |||
509 | self::assertTrue( |
||
510 | $this->object->readCredentials(), |
||
511 | ); |
||
512 | |||
513 | self::assertTrue(AuthenticationCookie::$fromCookie); |
||
514 | |||
515 | self::assertSame('', $this->object->password); |
||
516 | } |
||
517 | |||
518 | public function testAuthCheckAuthFails(): void |
||
519 | { |
||
520 | $_REQUEST['old_usr'] = ''; |
||
521 | $_POST['pma_username'] = ''; |
||
522 | $_COOKIE['pmaServer-1'] = 'pmaServ1'; |
||
523 | $_COOKIE['pmaUser-1'] = 'pmaUser1'; |
||
524 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
525 | $config = Config::getInstance(); |
||
526 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
527 | $_SESSION['last_access_time'] = 1; |
||
528 | $config->settings['CaptchaApi'] = ''; |
||
529 | $config->settings['CaptchaRequestParam'] = ''; |
||
530 | $config->settings['CaptchaResponseParam'] = ''; |
||
531 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
532 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
533 | $config->settings['LoginCookieValidity'] = 0; |
||
534 | $_SESSION['browser_access_time']['default'] = -1; |
||
535 | |||
536 | // mock for blowfish function |
||
537 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
538 | ->disableOriginalConstructor() |
||
539 | ->onlyMethods(['cookieDecrypt']) |
||
540 | ->getMock(); |
||
541 | |||
542 | $this->object->expects(self::once()) |
||
543 | ->method('cookieDecrypt') |
||
544 | ->willReturn('testBF'); |
||
545 | |||
546 | $this->expectExceptionObject(AuthenticationFailure::loggedOutDueToInactivity()); |
||
547 | $this->object->readCredentials(); |
||
548 | } |
||
549 | |||
550 | public function testAuthSetUser(): void |
||
551 | { |
||
552 | $this->object->user = 'pmaUser2'; |
||
553 | $arr = ['host' => 'a', 'port' => 1, 'socket' => true, 'ssl' => true, 'user' => 'pmaUser2']; |
||
554 | |||
555 | $config = Config::getInstance(); |
||
556 | $config->selectedServer = $arr; |
||
557 | $config->selectedServer['user'] = 'pmaUser'; |
||
558 | $config->settings['Servers'][1] = $arr; |
||
559 | $config->settings['AllowArbitraryServer'] = true; |
||
560 | AuthenticationCookie::$authServer = 'b 2'; |
||
561 | $this->object->password = 'testPW'; |
||
562 | Current::$server = 2; |
||
563 | $config->settings['LoginCookieStore'] = 100; |
||
564 | AuthenticationCookie::$fromCookie = true; |
||
565 | |||
566 | $this->object->storeCredentials(); |
||
567 | |||
568 | $this->object->rememberCredentials(); |
||
569 | |||
570 | self::assertArrayHasKey('pmaUser-2', $_COOKIE); |
||
571 | |||
572 | self::assertArrayHasKey('pmaAuth-2', $_COOKIE); |
||
573 | |||
574 | $arr['password'] = 'testPW'; |
||
575 | $arr['host'] = 'b'; |
||
576 | $arr['port'] = '2'; |
||
577 | self::assertSame($arr, $config->selectedServer); |
||
578 | } |
||
579 | |||
580 | public function testAuthSetUserWithHeaders(): void |
||
581 | { |
||
582 | $this->object->user = 'pmaUser2'; |
||
583 | $arr = ['host' => 'a', 'port' => 1, 'socket' => true, 'ssl' => true, 'user' => 'pmaUser2']; |
||
584 | |||
585 | $config = Config::getInstance(); |
||
586 | $config->selectedServer = $arr; |
||
587 | $config->selectedServer['host'] = 'b'; |
||
588 | $config->selectedServer['user'] = 'pmaUser'; |
||
589 | $config->settings['Servers'][1] = $arr; |
||
590 | $config->settings['AllowArbitraryServer'] = true; |
||
591 | $config->set('PmaAbsoluteUri', 'http://localhost/phpmyadmin'); |
||
592 | AuthenticationCookie::$authServer = 'b 2'; |
||
593 | $this->object->password = 'testPW'; |
||
594 | $config->settings['LoginCookieStore'] = 100; |
||
595 | AuthenticationCookie::$fromCookie = false; |
||
596 | |||
597 | $responseStub = new ResponseRendererStub(); |
||
598 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
599 | |||
600 | $this->object->storeCredentials(); |
||
601 | $response = $this->object->rememberCredentials(); |
||
602 | self::assertNotNull($response); |
||
603 | self::assertSame(StatusCodeInterface::STATUS_FOUND, $response->getStatusCode()); |
||
604 | self::assertStringEndsWith( |
||
605 | '/phpmyadmin/index.php?route=/&db=db&table=table&lang=en', |
||
606 | $response->getHeaderLine('Location'), |
||
607 | ); |
||
608 | } |
||
609 | |||
610 | public function testAuthFailsNoPass(): void |
||
611 | { |
||
612 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
613 | ->disableOriginalConstructor() |
||
614 | ->onlyMethods(['showLoginForm']) |
||
615 | ->getMock(); |
||
616 | |||
617 | $this->object->expects(self::exactly(1)) |
||
618 | ->method('showLoginForm') |
||
619 | ->willThrowException(new ExitException()); |
||
620 | |||
621 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
622 | |||
623 | $responseStub = new ResponseRendererStub(); |
||
624 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
625 | |||
626 | try { |
||
627 | $this->object->showFailure(AuthenticationFailure::emptyPasswordDeniedByConfiguration()); |
||
628 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
![]() |
|||
629 | } |
||
630 | |||
631 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
632 | $response = $responseStub->getResponse(); |
||
633 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
634 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
635 | self::assertSame(200, $response->getStatusCode()); |
||
636 | |||
637 | self::assertSame( |
||
638 | 'Login without a password is forbidden by configuration (see AllowNoPassword).', |
||
639 | AuthenticationCookie::$connectionError, |
||
640 | ); |
||
641 | } |
||
642 | |||
643 | /** @return mixed[] */ |
||
644 | public static function dataProviderPasswordLength(): array |
||
645 | { |
||
646 | return [ |
||
647 | [ |
||
648 | str_repeat('a', 2001), |
||
649 | false, |
||
650 | 'Your password is too long. To prevent denial-of-service attacks,' |
||
651 | . ' phpMyAdmin restricts passwords to less than 2000 characters.', |
||
652 | ], |
||
653 | [ |
||
654 | str_repeat('a', 3000), |
||
655 | false, |
||
656 | 'Your password is too long. To prevent denial-of-service attacks,' |
||
657 | . ' phpMyAdmin restricts passwords to less than 2000 characters.', |
||
658 | ], |
||
659 | [str_repeat('a', 256), true, ''], |
||
660 | ['', true, ''], |
||
661 | ]; |
||
662 | } |
||
663 | |||
664 | #[DataProvider('dataProviderPasswordLength')] |
||
665 | public function testAuthFailsTooLongPass(string $password, bool $expected, string $connError): void |
||
666 | { |
||
667 | $_POST['pma_username'] = str_shuffle('123456987rootfoobar'); |
||
668 | $_POST['pma_password'] = $password; |
||
669 | |||
670 | self::assertSame( |
||
671 | $expected, |
||
672 | $this->object->readCredentials(), |
||
673 | ); |
||
674 | |||
675 | self::assertSame($connError, AuthenticationCookie::$connectionError); |
||
676 | } |
||
677 | |||
678 | public function testAuthFailsDeny(): void |
||
679 | { |
||
680 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
681 | ->disableOriginalConstructor() |
||
682 | ->onlyMethods(['showLoginForm']) |
||
683 | ->getMock(); |
||
684 | |||
685 | $this->object->expects(self::exactly(1)) |
||
686 | ->method('showLoginForm') |
||
687 | ->willThrowException(new ExitException()); |
||
688 | |||
689 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
690 | |||
691 | $responseStub = new ResponseRendererStub(); |
||
692 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
693 | |||
694 | try { |
||
695 | $this->object->showFailure(AuthenticationFailure::deniedByAllowDenyRules()); |
||
696 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
697 | } |
||
698 | |||
699 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
700 | $response = $responseStub->getResponse(); |
||
701 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
702 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
703 | self::assertSame(200, $response->getStatusCode()); |
||
704 | |||
705 | self::assertSame('Access denied!', AuthenticationCookie::$connectionError); |
||
706 | } |
||
707 | |||
708 | public function testAuthFailsActivity(): void |
||
709 | { |
||
710 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
711 | ->disableOriginalConstructor() |
||
712 | ->onlyMethods(['showLoginForm']) |
||
713 | ->getMock(); |
||
714 | |||
715 | $this->object->expects(self::exactly(1)) |
||
716 | ->method('showLoginForm') |
||
717 | ->willThrowException(new ExitException()); |
||
718 | |||
719 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
720 | |||
721 | Config::getInstance()->settings['LoginCookieValidity'] = 10; |
||
722 | |||
723 | $responseStub = new ResponseRendererStub(); |
||
724 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
725 | |||
726 | try { |
||
727 | $this->object->showFailure(AuthenticationFailure::loggedOutDueToInactivity()); |
||
728 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
729 | } |
||
730 | |||
731 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
732 | $response = $responseStub->getResponse(); |
||
733 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
734 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
735 | self::assertSame(200, $response->getStatusCode()); |
||
736 | |||
737 | self::assertSame( |
||
738 | 'You have been automatically logged out due to inactivity of 10 seconds.' |
||
739 | . ' Once you log in again, you should be able to resume the work where you left off.', |
||
740 | AuthenticationCookie::$connectionError, |
||
741 | ); |
||
742 | } |
||
743 | |||
744 | public function testAuthFailsDBI(): void |
||
745 | { |
||
746 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
747 | ->disableOriginalConstructor() |
||
748 | ->onlyMethods(['showLoginForm']) |
||
749 | ->getMock(); |
||
750 | |||
751 | $this->object->expects(self::exactly(1)) |
||
752 | ->method('showLoginForm') |
||
753 | ->willThrowException(new ExitException()); |
||
754 | |||
755 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
756 | |||
757 | $dbi = $this->getMockBuilder(DatabaseInterface::class) |
||
758 | ->disableOriginalConstructor() |
||
759 | ->getMock(); |
||
760 | |||
761 | $dbi->expects(self::once()) |
||
762 | ->method('getError') |
||
763 | ->willReturn(''); |
||
764 | |||
765 | DatabaseInterface::$instance = $dbi; |
||
766 | DatabaseInterface::$errorNumber = 42; |
||
767 | |||
768 | $responseStub = new ResponseRendererStub(); |
||
769 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
770 | |||
771 | try { |
||
772 | $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer()); |
||
773 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
774 | } |
||
775 | |||
776 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
777 | $response = $responseStub->getResponse(); |
||
778 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
779 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
780 | self::assertSame(200, $response->getStatusCode()); |
||
781 | |||
782 | self::assertSame('#42 Cannot log in to the database server.', AuthenticationCookie::$connectionError); |
||
783 | } |
||
784 | |||
785 | public function testAuthFailsErrno(): void |
||
786 | { |
||
787 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
788 | ->disableOriginalConstructor() |
||
789 | ->onlyMethods(['showLoginForm']) |
||
790 | ->getMock(); |
||
791 | |||
792 | $this->object->expects(self::exactly(1)) |
||
793 | ->method('showLoginForm') |
||
794 | ->willThrowException(new ExitException()); |
||
795 | |||
796 | $dbi = $this->getMockBuilder(DatabaseInterface::class) |
||
797 | ->disableOriginalConstructor() |
||
798 | ->getMock(); |
||
799 | |||
800 | $dbi->expects(self::once()) |
||
801 | ->method('getError') |
||
802 | ->willReturn(''); |
||
803 | |||
804 | DatabaseInterface::$instance = $dbi; |
||
805 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
806 | |||
807 | DatabaseInterface::$errorNumber = null; |
||
808 | |||
809 | $responseStub = new ResponseRendererStub(); |
||
810 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
811 | |||
812 | try { |
||
813 | $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer()); |
||
814 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
815 | } |
||
816 | |||
817 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
818 | $response = $responseStub->getResponse(); |
||
819 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
820 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
821 | self::assertSame(200, $response->getStatusCode()); |
||
822 | |||
823 | self::assertSame('Cannot log in to the database server.', AuthenticationCookie::$connectionError); |
||
824 | } |
||
825 | |||
826 | public function testGetEncryptionSecretEmpty(): void |
||
827 | { |
||
828 | $method = new ReflectionMethod(AuthenticationCookie::class, 'getEncryptionSecret'); |
||
829 | |||
830 | Config::getInstance()->settings['blowfish_secret'] = ''; |
||
831 | $_SESSION['encryption_key'] = ''; |
||
832 | |||
833 | $result = $method->invoke($this->object, null); |
||
834 | |||
835 | self::assertSame($result, $_SESSION['encryption_key']); |
||
836 | self::assertSame(SODIUM_CRYPTO_SECRETBOX_KEYBYTES, mb_strlen($result, '8bit')); |
||
837 | } |
||
838 | |||
839 | public function testGetEncryptionSecretConfigured(): void |
||
840 | { |
||
841 | $method = new ReflectionMethod(AuthenticationCookie::class, 'getEncryptionSecret'); |
||
842 | |||
843 | $key = str_repeat('a', SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
844 | Config::getInstance()->settings['blowfish_secret'] = $key; |
||
845 | $_SESSION['encryption_key'] = ''; |
||
846 | |||
847 | $result = $method->invoke($this->object, null); |
||
848 | |||
849 | self::assertSame($key, $result); |
||
850 | } |
||
851 | |||
852 | public function testGetSessionEncryptionSecretConfigured(): void |
||
853 | { |
||
854 | $method = new ReflectionMethod(AuthenticationCookie::class, 'getEncryptionSecret'); |
||
855 | |||
856 | $key = str_repeat('a', SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
857 | Config::getInstance()->settings['blowfish_secret'] = 'blowfish_secret'; |
||
858 | $_SESSION['encryption_key'] = $key; |
||
859 | |||
860 | $result = $method->invoke($this->object, null); |
||
861 | |||
862 | self::assertSame($key, $result); |
||
863 | } |
||
864 | |||
865 | public function testCookieEncryption(): void |
||
866 | { |
||
867 | $key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
868 | $encrypted = $this->object->cookieEncrypt('data123', $key); |
||
869 | self::assertNotFalse(base64_decode($encrypted, true)); |
||
870 | self::assertSame('data123', $this->object->cookieDecrypt($encrypted, $key)); |
||
871 | } |
||
872 | |||
873 | public function testCookieDecryptInvalid(): void |
||
874 | { |
||
875 | self::assertNull($this->object->cookieDecrypt('', '')); |
||
876 | |||
877 | $key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
878 | $encrypted = $this->object->cookieEncrypt('data123', $key); |
||
879 | self::assertSame('data123', $this->object->cookieDecrypt($encrypted, $key)); |
||
880 | |||
881 | self::assertNull($this->object->cookieDecrypt('', $key)); |
||
882 | self::assertNull($this->object->cookieDecrypt($encrypted, '')); |
||
883 | self::assertNull($this->object->cookieDecrypt($encrypted, random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES))); |
||
884 | } |
||
885 | |||
886 | /** @throws ReflectionException */ |
||
887 | public function testPasswordChange(): void |
||
888 | { |
||
889 | $newPassword = 'PMAPASSWD2'; |
||
890 | $config = Config::getInstance(); |
||
891 | $config->settings['AllowArbitraryServer'] = true; |
||
892 | AuthenticationCookie::$authServer = 'b 2'; |
||
893 | $_SESSION['encryption_key'] = ''; |
||
894 | $_COOKIE = []; |
||
895 | |||
896 | $this->object->handlePasswordChange($newPassword); |
||
897 | |||
898 | $payload = ['password' => $newPassword, 'server' => 'b 2']; |
||
899 | |||
900 | /** @psalm-suppress EmptyArrayAccess */ |
||
901 | self::assertIsString($_COOKIE['pmaAuth-' . Current::$server]); |
||
902 | $decryptedCookie = $this->object->cookieDecrypt( |
||
903 | $_COOKIE['pmaAuth-' . Current::$server], |
||
904 | $_SESSION['encryption_key'], |
||
905 | ); |
||
906 | self::assertSame(json_encode($payload), $decryptedCookie); |
||
907 | } |
||
908 | |||
909 | public function testAuthenticate(): void |
||
910 | { |
||
911 | $config = Config::getInstance(); |
||
912 | $config->settings['CaptchaApi'] = ''; |
||
913 | $config->settings['CaptchaRequestParam'] = ''; |
||
914 | $config->settings['CaptchaResponseParam'] = ''; |
||
915 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
916 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
917 | $config->selectedServer['AllowRoot'] = false; |
||
918 | $config->selectedServer['AllowNoPassword'] = false; |
||
919 | $_REQUEST['old_usr'] = ''; |
||
920 | $_POST['pma_username'] = 'testUser'; |
||
921 | $_POST['pma_password'] = 'testPassword'; |
||
922 | |||
923 | ob_start(); |
||
924 | $response = $this->object->authenticate(); |
||
925 | $result = ob_get_clean(); |
||
926 | |||
927 | self::assertNull($response); |
||
928 | /* Nothing should be printed */ |
||
929 | self::assertSame('', $result); |
||
930 | |||
931 | /* Verify readCredentials worked */ |
||
932 | self::assertSame('testUser', $this->object->user); |
||
933 | self::assertSame('testPassword', $this->object->password); |
||
934 | |||
935 | /* Verify storeCredentials worked */ |
||
936 | self::assertSame('testUser', $config->selectedServer['user']); |
||
937 | self::assertSame('testPassword', $config->selectedServer['password']); |
||
938 | } |
||
939 | |||
940 | /** |
||
941 | * @param string $user user |
||
942 | * @param string $pass pass |
||
943 | * @param string $ip ip |
||
944 | * @param bool $root root |
||
945 | * @param bool $nopass nopass |
||
946 | * @param mixed[] $rules rules |
||
947 | * @param string $expected expected result |
||
948 | */ |
||
949 | #[DataProvider('checkRulesProvider')] |
||
950 | public function testCheckRules( |
||
951 | string $user, |
||
952 | string $pass, |
||
953 | string $ip, |
||
954 | bool $root, |
||
955 | bool $nopass, |
||
956 | array $rules, |
||
957 | string $expected, |
||
958 | ): void { |
||
959 | $this->object->user = $user; |
||
960 | $this->object->password = $pass; |
||
961 | $this->object->storeCredentials(); |
||
962 | |||
963 | $_SERVER['REMOTE_ADDR'] = $ip; |
||
964 | |||
965 | $config = Config::getInstance(); |
||
966 | $config->selectedServer['AllowRoot'] = $root; |
||
967 | $config->selectedServer['AllowNoPassword'] = $nopass; |
||
968 | $config->selectedServer['AllowDeny'] = $rules; |
||
969 | |||
970 | $exception = null; |
||
971 | try { |
||
972 | $this->object->checkRules(); |
||
973 | } catch (AuthenticationFailure $exception) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
974 | } |
||
975 | |||
976 | if ($expected === '') { |
||
977 | self::assertNull($exception, 'checkRules() should not throw an exception.'); |
||
978 | |||
979 | return; |
||
980 | } |
||
981 | |||
982 | self::assertInstanceOf(AuthenticationFailure::class, $exception); |
||
983 | self::assertSame($expected, $exception->failureType); |
||
984 | } |
||
985 | |||
986 | /** @return mixed[] */ |
||
987 | public static function checkRulesProvider(): array |
||
988 | { |
||
989 | return [ |
||
990 | 'nopass-ok' => ['testUser', '', '1.2.3.4', true, true, [], ''], |
||
991 | 'nopass' => ['testUser', '', '1.2.3.4', true, false, [], AuthenticationFailure::EMPTY_DENIED], |
||
992 | 'root-ok' => ['root', 'root', '1.2.3.4', true, true, [], ''], |
||
993 | 'root' => ['root', 'root', '1.2.3.4', false, true, [], AuthenticationFailure::ROOT_DENIED], |
||
994 | 'rules-deny-allow-ok' => [ |
||
995 | 'root', |
||
996 | 'root', |
||
997 | '1.2.3.4', |
||
998 | true, |
||
999 | true, |
||
1000 | ['order' => 'deny,allow', 'rules' => ['allow root 1.2.3.4', 'deny % from all']], |
||
1001 | '', |
||
1002 | ], |
||
1003 | 'rules-deny-allow-reject' => [ |
||
1004 | 'user', |
||
1005 | 'root', |
||
1006 | '1.2.3.4', |
||
1007 | true, |
||
1008 | true, |
||
1009 | ['order' => 'deny,allow', 'rules' => ['allow root 1.2.3.4', 'deny % from all']], |
||
1010 | AuthenticationFailure::ALLOW_DENIED, |
||
1011 | ], |
||
1012 | 'rules-allow-deny-ok' => [ |
||
1013 | 'root', |
||
1014 | 'root', |
||
1015 | '1.2.3.4', |
||
1016 | true, |
||
1017 | true, |
||
1018 | ['order' => 'allow,deny', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1019 | '', |
||
1020 | ], |
||
1021 | 'rules-allow-deny-reject' => [ |
||
1022 | 'user', |
||
1023 | 'root', |
||
1024 | '1.2.3.4', |
||
1025 | true, |
||
1026 | true, |
||
1027 | ['order' => 'allow,deny', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1028 | AuthenticationFailure::ALLOW_DENIED, |
||
1029 | ], |
||
1030 | 'rules-explicit-ok' => [ |
||
1031 | 'root', |
||
1032 | 'root', |
||
1033 | '1.2.3.4', |
||
1034 | true, |
||
1035 | true, |
||
1036 | ['order' => 'explicit', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1037 | '', |
||
1038 | ], |
||
1039 | 'rules-explicit-reject' => [ |
||
1040 | 'user', |
||
1041 | 'root', |
||
1042 | '1.2.3.4', |
||
1043 | true, |
||
1044 | true, |
||
1045 | ['order' => 'explicit', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1046 | AuthenticationFailure::ALLOW_DENIED, |
||
1047 | ], |
||
1048 | ]; |
||
1049 | } |
||
1050 | } |
||
1051 |