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\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 | $GLOBALS['conn_error'] = null; |
||
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 | $GLOBALS['conn_error'] = true; |
||
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 | $GLOBALS['pma_auth_server'] = 'localhost'; |
||
108 | |||
109 | $GLOBALS['conn_error'] = true; |
||
110 | $config->settings['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->settings['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->settings['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->set('is_https', false); |
||
301 | $config->settings['LoginCookieDeleteAll'] = false; |
||
302 | $config->settings['Servers'] = [1, 2, 3]; |
||
303 | $config->selectedServer['LogoutURL'] = 'https://example.com/logout'; |
||
304 | $config->selectedServer['auth_type'] = 'cookie'; |
||
305 | |||
306 | $_COOKIE['pmaAuth-2'] = ''; |
||
307 | |||
308 | $responseStub = new ResponseRendererStub(); |
||
309 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
310 | |||
311 | $this->object->logOut(); |
||
312 | |||
313 | $response = $responseStub->getResponse(); |
||
314 | self::assertSame(['/phpmyadmin/index.php?route=/&server=2&lang=en'], $response->getHeader('Location')); |
||
315 | self::assertSame(302, $response->getStatusCode()); |
||
316 | } |
||
317 | |||
318 | public function testAuthCheckCaptcha(): void |
||
319 | { |
||
320 | $config = Config::getInstance(); |
||
321 | $config->settings['CaptchaApi'] = 'https://www.google.com/recaptcha/api.js'; |
||
322 | $config->settings['CaptchaRequestParam'] = 'g-recaptcha'; |
||
323 | $config->settings['CaptchaResponseParam'] = 'g-recaptcha-response'; |
||
324 | $config->settings['CaptchaLoginPrivateKey'] = 'testprivkey'; |
||
325 | $config->settings['CaptchaLoginPublicKey'] = 'testpubkey'; |
||
326 | $_POST['g-recaptcha-response'] = ''; |
||
327 | $_POST['pma_username'] = 'testPMAUser'; |
||
328 | |||
329 | self::assertFalse( |
||
330 | $this->object->readCredentials(), |
||
331 | ); |
||
332 | |||
333 | self::assertSame('Missing Captcha verification, maybe it has been blocked by adblock?', $GLOBALS['conn_error']); |
||
334 | } |
||
335 | |||
336 | public function testLogoutDelete(): void |
||
337 | { |
||
338 | $responseStub = new ResponseRendererStub(); |
||
339 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
340 | |||
341 | $config = Config::getInstance(); |
||
342 | $config->settings['CaptchaApi'] = ''; |
||
343 | $config->settings['CaptchaRequestParam'] = ''; |
||
344 | $config->settings['CaptchaResponseParam'] = ''; |
||
345 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
346 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
347 | $config->settings['LoginCookieDeleteAll'] = true; |
||
348 | $config->set('PmaAbsoluteUri', ''); |
||
349 | $config->set('is_https', false); |
||
350 | $config->settings['Servers'] = [1]; |
||
351 | |||
352 | $_COOKIE['pmaAuth-0'] = 'test'; |
||
353 | |||
354 | $this->object->logOut(); |
||
355 | |||
356 | $response = $responseStub->getResponse(); |
||
357 | self::assertSame(['/phpmyadmin/index.php?route=/'], $response->getHeader('Location')); |
||
358 | self::assertSame(302, $response->getStatusCode()); |
||
359 | |||
360 | self::assertArrayNotHasKey('pmaAuth-0', $_COOKIE); |
||
361 | } |
||
362 | |||
363 | public function testLogout(): void |
||
364 | { |
||
365 | $responseStub = new ResponseRendererStub(); |
||
366 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
367 | |||
368 | $config = Config::getInstance(); |
||
369 | $config->settings['CaptchaApi'] = ''; |
||
370 | $config->settings['CaptchaRequestParam'] = ''; |
||
371 | $config->settings['CaptchaResponseParam'] = ''; |
||
372 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
373 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
374 | $config->settings['LoginCookieDeleteAll'] = false; |
||
375 | $config->set('PmaAbsoluteUri', ''); |
||
376 | $config->set('is_https', false); |
||
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', $GLOBALS['pma_auth_server']); |
||
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 | $config->set('is_https', false); |
||
464 | |||
465 | // mock for blowfish function |
||
466 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
467 | ->disableOriginalConstructor() |
||
468 | ->onlyMethods(['cookieDecrypt']) |
||
469 | ->getMock(); |
||
470 | |||
471 | $this->object->expects(self::once()) |
||
472 | ->method('cookieDecrypt') |
||
473 | ->willReturn('testBF'); |
||
474 | |||
475 | self::assertFalse( |
||
476 | $this->object->readCredentials(), |
||
477 | ); |
||
478 | |||
479 | self::assertSame('testBF', $this->object->user); |
||
480 | } |
||
481 | |||
482 | public function testAuthCheckDecryptPassword(): void |
||
483 | { |
||
484 | $_REQUEST['old_usr'] = ''; |
||
485 | $_POST['pma_username'] = ''; |
||
486 | $_COOKIE['pmaServer-1'] = 'pmaServ1'; |
||
487 | $_COOKIE['pmaUser-1'] = 'pmaUser1'; |
||
488 | $_COOKIE['pmaAuth-1'] = 'pmaAuth1'; |
||
489 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
490 | $config = Config::getInstance(); |
||
491 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
492 | $config->settings['CaptchaApi'] = ''; |
||
493 | $config->settings['CaptchaRequestParam'] = ''; |
||
494 | $config->settings['CaptchaResponseParam'] = ''; |
||
495 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
496 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
497 | $_SESSION['browser_access_time']['default'] = time() - 1000; |
||
498 | $config->settings['LoginCookieValidity'] = 1440; |
||
499 | $config->set('is_https', false); |
||
500 | |||
501 | // mock for blowfish function |
||
502 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
503 | ->disableOriginalConstructor() |
||
504 | ->onlyMethods(['cookieDecrypt']) |
||
505 | ->getMock(); |
||
506 | |||
507 | $this->object->expects(self::exactly(2)) |
||
508 | ->method('cookieDecrypt') |
||
509 | ->willReturn('{"password":""}'); |
||
510 | |||
511 | self::assertTrue( |
||
512 | $this->object->readCredentials(), |
||
513 | ); |
||
514 | |||
515 | self::assertTrue($GLOBALS['from_cookie']); |
||
516 | |||
517 | self::assertSame('', $this->object->password); |
||
518 | } |
||
519 | |||
520 | public function testAuthCheckAuthFails(): void |
||
521 | { |
||
522 | $_REQUEST['old_usr'] = ''; |
||
523 | $_POST['pma_username'] = ''; |
||
524 | $_COOKIE['pmaServer-1'] = 'pmaServ1'; |
||
525 | $_COOKIE['pmaUser-1'] = 'pmaUser1'; |
||
526 | $_COOKIE['pma_iv-1'] = base64_encode('testiv09testiv09'); |
||
527 | $config = Config::getInstance(); |
||
528 | $config->settings['blowfish_secret'] = str_repeat('a', 32); |
||
529 | $_SESSION['last_access_time'] = 1; |
||
530 | $config->settings['CaptchaApi'] = ''; |
||
531 | $config->settings['CaptchaRequestParam'] = ''; |
||
532 | $config->settings['CaptchaResponseParam'] = ''; |
||
533 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
534 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
535 | $config->settings['LoginCookieValidity'] = 0; |
||
536 | $_SESSION['browser_access_time']['default'] = -1; |
||
537 | $config->set('is_https', false); |
||
538 | |||
539 | // mock for blowfish function |
||
540 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
541 | ->disableOriginalConstructor() |
||
542 | ->onlyMethods(['cookieDecrypt']) |
||
543 | ->getMock(); |
||
544 | |||
545 | $this->object->expects(self::once()) |
||
546 | ->method('cookieDecrypt') |
||
547 | ->willReturn('testBF'); |
||
548 | |||
549 | $this->expectExceptionObject(AuthenticationFailure::loggedOutDueToInactivity()); |
||
550 | $this->object->readCredentials(); |
||
551 | } |
||
552 | |||
553 | public function testAuthSetUser(): void |
||
554 | { |
||
555 | $this->object->user = 'pmaUser2'; |
||
556 | $arr = ['host' => 'a', 'port' => 1, 'socket' => true, 'ssl' => true, 'user' => 'pmaUser2']; |
||
557 | |||
558 | $config = Config::getInstance(); |
||
559 | $config->selectedServer = $arr; |
||
560 | $config->selectedServer['user'] = 'pmaUser'; |
||
561 | $config->settings['Servers'][1] = $arr; |
||
562 | $config->settings['AllowArbitraryServer'] = true; |
||
563 | $GLOBALS['pma_auth_server'] = 'b 2'; |
||
564 | $this->object->password = 'testPW'; |
||
565 | Current::$server = 2; |
||
566 | $config->settings['LoginCookieStore'] = 100; |
||
567 | $GLOBALS['from_cookie'] = true; |
||
568 | $config->set('is_https', false); |
||
569 | |||
570 | $this->object->storeCredentials(); |
||
571 | |||
572 | $this->object->rememberCredentials(); |
||
573 | |||
574 | self::assertArrayHasKey('pmaUser-2', $_COOKIE); |
||
575 | |||
576 | self::assertArrayHasKey('pmaAuth-2', $_COOKIE); |
||
577 | |||
578 | $arr['password'] = 'testPW'; |
||
579 | $arr['host'] = 'b'; |
||
580 | $arr['port'] = '2'; |
||
581 | self::assertSame($arr, $config->selectedServer); |
||
582 | } |
||
583 | |||
584 | public function testAuthSetUserWithHeaders(): void |
||
585 | { |
||
586 | $this->object->user = 'pmaUser2'; |
||
587 | $arr = ['host' => 'a', 'port' => 1, 'socket' => true, 'ssl' => true, 'user' => 'pmaUser2']; |
||
588 | |||
589 | $config = Config::getInstance(); |
||
590 | $config->selectedServer = $arr; |
||
591 | $config->selectedServer['host'] = 'b'; |
||
592 | $config->selectedServer['user'] = 'pmaUser'; |
||
593 | $config->settings['Servers'][1] = $arr; |
||
594 | $config->settings['AllowArbitraryServer'] = true; |
||
595 | $config->settings['PmaAbsoluteUri'] = 'http://localhost/phpmyadmin'; |
||
596 | $GLOBALS['pma_auth_server'] = 'b 2'; |
||
597 | $this->object->password = 'testPW'; |
||
598 | $config->settings['LoginCookieStore'] = 100; |
||
599 | $GLOBALS['from_cookie'] = false; |
||
600 | |||
601 | $responseStub = new ResponseRendererStub(); |
||
602 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
603 | |||
604 | $this->object->storeCredentials(); |
||
605 | $response = $this->object->rememberCredentials(); |
||
606 | self::assertNotNull($response); |
||
607 | self::assertSame(StatusCodeInterface::STATUS_FOUND, $response->getStatusCode()); |
||
608 | self::assertStringEndsWith( |
||
609 | '/phpmyadmin/index.php?route=/&db=db&table=table&lang=en', |
||
610 | $response->getHeaderLine('Location'), |
||
611 | ); |
||
612 | } |
||
613 | |||
614 | public function testAuthFailsNoPass(): void |
||
615 | { |
||
616 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
617 | ->disableOriginalConstructor() |
||
618 | ->onlyMethods(['showLoginForm']) |
||
619 | ->getMock(); |
||
620 | |||
621 | $this->object->expects(self::exactly(1)) |
||
622 | ->method('showLoginForm') |
||
623 | ->willThrowException(new ExitException()); |
||
624 | |||
625 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
626 | |||
627 | $responseStub = new ResponseRendererStub(); |
||
628 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
629 | |||
630 | try { |
||
631 | $this->object->showFailure(AuthenticationFailure::emptyPasswordDeniedByConfiguration()); |
||
632 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
Loading history...
|
|||
633 | } |
||
634 | |||
635 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
636 | $response = $responseStub->getResponse(); |
||
637 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
638 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
639 | self::assertSame(200, $response->getStatusCode()); |
||
640 | |||
641 | self::assertSame( |
||
642 | $GLOBALS['conn_error'], |
||
643 | 'Login without a password is forbidden by configuration (see AllowNoPassword).', |
||
644 | ); |
||
645 | } |
||
646 | |||
647 | /** @return mixed[] */ |
||
648 | public static function dataProviderPasswordLength(): array |
||
649 | { |
||
650 | return [ |
||
651 | [ |
||
652 | str_repeat('a', 2001), |
||
653 | false, |
||
654 | 'Your password is too long. To prevent denial-of-service attacks,' |
||
655 | . ' phpMyAdmin restricts passwords to less than 2000 characters.', |
||
656 | ], |
||
657 | [ |
||
658 | str_repeat('a', 3000), |
||
659 | false, |
||
660 | 'Your password is too long. To prevent denial-of-service attacks,' |
||
661 | . ' phpMyAdmin restricts passwords to less than 2000 characters.', |
||
662 | ], |
||
663 | [str_repeat('a', 256), true, null], |
||
664 | ['', true, null], |
||
665 | ]; |
||
666 | } |
||
667 | |||
668 | #[DataProvider('dataProviderPasswordLength')] |
||
669 | public function testAuthFailsTooLongPass(string $password, bool $expected, string|null $connError): void |
||
670 | { |
||
671 | $_POST['pma_username'] = str_shuffle('123456987rootfoobar'); |
||
672 | $_POST['pma_password'] = $password; |
||
673 | |||
674 | self::assertSame( |
||
675 | $expected, |
||
676 | $this->object->readCredentials(), |
||
677 | ); |
||
678 | |||
679 | self::assertSame($GLOBALS['conn_error'], $connError); |
||
680 | } |
||
681 | |||
682 | public function testAuthFailsDeny(): void |
||
683 | { |
||
684 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
685 | ->disableOriginalConstructor() |
||
686 | ->onlyMethods(['showLoginForm']) |
||
687 | ->getMock(); |
||
688 | |||
689 | $this->object->expects(self::exactly(1)) |
||
690 | ->method('showLoginForm') |
||
691 | ->willThrowException(new ExitException()); |
||
692 | |||
693 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
694 | |||
695 | $responseStub = new ResponseRendererStub(); |
||
696 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
697 | |||
698 | try { |
||
699 | $this->object->showFailure(AuthenticationFailure::deniedByAllowDenyRules()); |
||
700 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
701 | } |
||
702 | |||
703 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
704 | $response = $responseStub->getResponse(); |
||
705 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
706 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
707 | self::assertSame(200, $response->getStatusCode()); |
||
708 | |||
709 | self::assertSame($GLOBALS['conn_error'], 'Access denied!'); |
||
710 | } |
||
711 | |||
712 | public function testAuthFailsActivity(): void |
||
713 | { |
||
714 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
715 | ->disableOriginalConstructor() |
||
716 | ->onlyMethods(['showLoginForm']) |
||
717 | ->getMock(); |
||
718 | |||
719 | $this->object->expects(self::exactly(1)) |
||
720 | ->method('showLoginForm') |
||
721 | ->willThrowException(new ExitException()); |
||
722 | |||
723 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
724 | |||
725 | Config::getInstance()->settings['LoginCookieValidity'] = 10; |
||
726 | |||
727 | $responseStub = new ResponseRendererStub(); |
||
728 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
729 | |||
730 | try { |
||
731 | $this->object->showFailure(AuthenticationFailure::loggedOutDueToInactivity()); |
||
732 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
733 | } |
||
734 | |||
735 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
736 | $response = $responseStub->getResponse(); |
||
737 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
738 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
739 | self::assertSame(200, $response->getStatusCode()); |
||
740 | |||
741 | self::assertSame( |
||
742 | $GLOBALS['conn_error'], |
||
743 | 'You have been automatically logged out due to inactivity of 10 seconds.' |
||
744 | . ' Once you log in again, you should be able to resume the work where you left off.', |
||
745 | ); |
||
746 | } |
||
747 | |||
748 | public function testAuthFailsDBI(): void |
||
749 | { |
||
750 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
751 | ->disableOriginalConstructor() |
||
752 | ->onlyMethods(['showLoginForm']) |
||
753 | ->getMock(); |
||
754 | |||
755 | $this->object->expects(self::exactly(1)) |
||
756 | ->method('showLoginForm') |
||
757 | ->willThrowException(new ExitException()); |
||
758 | |||
759 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
760 | |||
761 | $dbi = $this->getMockBuilder(DatabaseInterface::class) |
||
762 | ->disableOriginalConstructor() |
||
763 | ->getMock(); |
||
764 | |||
765 | $dbi->expects(self::once()) |
||
766 | ->method('getError') |
||
767 | ->willReturn(''); |
||
768 | |||
769 | DatabaseInterface::$instance = $dbi; |
||
770 | $GLOBALS['errno'] = 42; |
||
771 | |||
772 | $responseStub = new ResponseRendererStub(); |
||
773 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
774 | |||
775 | try { |
||
776 | $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer()); |
||
777 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
778 | } |
||
779 | |||
780 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
781 | $response = $responseStub->getResponse(); |
||
782 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
783 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
784 | self::assertSame(200, $response->getStatusCode()); |
||
785 | |||
786 | self::assertSame($GLOBALS['conn_error'], '#42 Cannot log in to the database server.'); |
||
787 | } |
||
788 | |||
789 | public function testAuthFailsErrno(): void |
||
790 | { |
||
791 | $this->object = $this->getMockBuilder(AuthenticationCookie::class) |
||
792 | ->disableOriginalConstructor() |
||
793 | ->onlyMethods(['showLoginForm']) |
||
794 | ->getMock(); |
||
795 | |||
796 | $this->object->expects(self::exactly(1)) |
||
797 | ->method('showLoginForm') |
||
798 | ->willThrowException(new ExitException()); |
||
799 | |||
800 | $dbi = $this->getMockBuilder(DatabaseInterface::class) |
||
801 | ->disableOriginalConstructor() |
||
802 | ->getMock(); |
||
803 | |||
804 | $dbi->expects(self::once()) |
||
805 | ->method('getError') |
||
806 | ->willReturn(''); |
||
807 | |||
808 | DatabaseInterface::$instance = $dbi; |
||
809 | $_COOKIE['pmaAuth-2'] = 'pass'; |
||
810 | |||
811 | unset($GLOBALS['errno']); |
||
812 | |||
813 | $responseStub = new ResponseRendererStub(); |
||
814 | (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, $responseStub); |
||
815 | |||
816 | try { |
||
817 | $this->object->showFailure(AuthenticationFailure::deniedByDatabaseServer()); |
||
818 | } catch (Throwable $throwable) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
819 | } |
||
820 | |||
821 | self::assertInstanceOf(ExitException::class, $throwable ?? null); |
||
822 | $response = $responseStub->getResponse(); |
||
823 | self::assertSame(['no-store, no-cache, must-revalidate'], $response->getHeader('Cache-Control')); |
||
824 | self::assertSame(['no-cache'], $response->getHeader('Pragma')); |
||
825 | self::assertSame(200, $response->getStatusCode()); |
||
826 | |||
827 | self::assertSame($GLOBALS['conn_error'], 'Cannot log in to the database server.'); |
||
828 | } |
||
829 | |||
830 | public function testGetEncryptionSecretEmpty(): void |
||
831 | { |
||
832 | $method = new ReflectionMethod(AuthenticationCookie::class, 'getEncryptionSecret'); |
||
833 | |||
834 | Config::getInstance()->settings['blowfish_secret'] = ''; |
||
835 | $_SESSION['encryption_key'] = ''; |
||
836 | |||
837 | $result = $method->invoke($this->object, null); |
||
838 | |||
839 | self::assertSame($result, $_SESSION['encryption_key']); |
||
840 | self::assertSame(SODIUM_CRYPTO_SECRETBOX_KEYBYTES, mb_strlen($result, '8bit')); |
||
841 | } |
||
842 | |||
843 | public function testGetEncryptionSecretConfigured(): void |
||
844 | { |
||
845 | $method = new ReflectionMethod(AuthenticationCookie::class, 'getEncryptionSecret'); |
||
846 | |||
847 | $key = str_repeat('a', SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
848 | Config::getInstance()->settings['blowfish_secret'] = $key; |
||
849 | $_SESSION['encryption_key'] = ''; |
||
850 | |||
851 | $result = $method->invoke($this->object, null); |
||
852 | |||
853 | self::assertSame($key, $result); |
||
854 | } |
||
855 | |||
856 | public function testGetSessionEncryptionSecretConfigured(): void |
||
857 | { |
||
858 | $method = new ReflectionMethod(AuthenticationCookie::class, 'getEncryptionSecret'); |
||
859 | |||
860 | $key = str_repeat('a', SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
861 | Config::getInstance()->settings['blowfish_secret'] = 'blowfish_secret'; |
||
862 | $_SESSION['encryption_key'] = $key; |
||
863 | |||
864 | $result = $method->invoke($this->object, null); |
||
865 | |||
866 | self::assertSame($key, $result); |
||
867 | } |
||
868 | |||
869 | public function testCookieEncryption(): void |
||
870 | { |
||
871 | $key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
872 | $encrypted = $this->object->cookieEncrypt('data123', $key); |
||
873 | self::assertNotFalse(base64_decode($encrypted, true)); |
||
874 | self::assertSame('data123', $this->object->cookieDecrypt($encrypted, $key)); |
||
875 | } |
||
876 | |||
877 | public function testCookieDecryptInvalid(): void |
||
878 | { |
||
879 | self::assertNull($this->object->cookieDecrypt('', '')); |
||
880 | |||
881 | $key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
||
882 | $encrypted = $this->object->cookieEncrypt('data123', $key); |
||
883 | self::assertSame('data123', $this->object->cookieDecrypt($encrypted, $key)); |
||
884 | |||
885 | self::assertNull($this->object->cookieDecrypt('', $key)); |
||
886 | self::assertNull($this->object->cookieDecrypt($encrypted, '')); |
||
887 | self::assertNull($this->object->cookieDecrypt($encrypted, random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES))); |
||
888 | } |
||
889 | |||
890 | /** @throws ReflectionException */ |
||
891 | public function testPasswordChange(): void |
||
892 | { |
||
893 | $newPassword = 'PMAPASSWD2'; |
||
894 | $config = Config::getInstance(); |
||
895 | $config->set('is_https', false); |
||
896 | $config->settings['AllowArbitraryServer'] = true; |
||
897 | $GLOBALS['pma_auth_server'] = 'b 2'; |
||
898 | $_SESSION['encryption_key'] = ''; |
||
899 | $_COOKIE = []; |
||
900 | |||
901 | $this->object->handlePasswordChange($newPassword); |
||
902 | |||
903 | $payload = ['password' => $newPassword, 'server' => 'b 2']; |
||
904 | |||
905 | /** @psalm-suppress EmptyArrayAccess */ |
||
906 | self::assertIsString($_COOKIE['pmaAuth-' . Current::$server]); |
||
907 | $decryptedCookie = $this->object->cookieDecrypt( |
||
908 | $_COOKIE['pmaAuth-' . Current::$server], |
||
909 | $_SESSION['encryption_key'], |
||
910 | ); |
||
911 | self::assertSame(json_encode($payload), $decryptedCookie); |
||
912 | } |
||
913 | |||
914 | public function testAuthenticate(): void |
||
915 | { |
||
916 | $config = Config::getInstance(); |
||
917 | $config->settings['CaptchaApi'] = ''; |
||
918 | $config->settings['CaptchaRequestParam'] = ''; |
||
919 | $config->settings['CaptchaResponseParam'] = ''; |
||
920 | $config->settings['CaptchaLoginPrivateKey'] = ''; |
||
921 | $config->settings['CaptchaLoginPublicKey'] = ''; |
||
922 | $config->selectedServer['AllowRoot'] = false; |
||
923 | $config->selectedServer['AllowNoPassword'] = false; |
||
924 | $_REQUEST['old_usr'] = ''; |
||
925 | $_POST['pma_username'] = 'testUser'; |
||
926 | $_POST['pma_password'] = 'testPassword'; |
||
927 | |||
928 | ob_start(); |
||
929 | $response = $this->object->authenticate(); |
||
930 | $result = ob_get_clean(); |
||
931 | |||
932 | self::assertNull($response); |
||
933 | /* Nothing should be printed */ |
||
934 | self::assertSame('', $result); |
||
935 | |||
936 | /* Verify readCredentials worked */ |
||
937 | self::assertSame('testUser', $this->object->user); |
||
938 | self::assertSame('testPassword', $this->object->password); |
||
939 | |||
940 | /* Verify storeCredentials worked */ |
||
941 | self::assertSame('testUser', $config->selectedServer['user']); |
||
942 | self::assertSame('testPassword', $config->selectedServer['password']); |
||
943 | } |
||
944 | |||
945 | /** |
||
946 | * @param string $user user |
||
947 | * @param string $pass pass |
||
948 | * @param string $ip ip |
||
949 | * @param bool $root root |
||
950 | * @param bool $nopass nopass |
||
951 | * @param mixed[] $rules rules |
||
952 | * @param string $expected expected result |
||
953 | */ |
||
954 | #[DataProvider('checkRulesProvider')] |
||
955 | public function testCheckRules( |
||
956 | string $user, |
||
957 | string $pass, |
||
958 | string $ip, |
||
959 | bool $root, |
||
960 | bool $nopass, |
||
961 | array $rules, |
||
962 | string $expected, |
||
963 | ): void { |
||
964 | $this->object->user = $user; |
||
965 | $this->object->password = $pass; |
||
966 | $this->object->storeCredentials(); |
||
967 | |||
968 | $_SERVER['REMOTE_ADDR'] = $ip; |
||
969 | |||
970 | $config = Config::getInstance(); |
||
971 | $config->selectedServer['AllowRoot'] = $root; |
||
972 | $config->selectedServer['AllowNoPassword'] = $nopass; |
||
973 | $config->selectedServer['AllowDeny'] = $rules; |
||
974 | |||
975 | $exception = null; |
||
976 | try { |
||
977 | $this->object->checkRules(); |
||
978 | } catch (AuthenticationFailure $exception) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
979 | } |
||
980 | |||
981 | if ($expected === '') { |
||
982 | self::assertNull($exception, 'checkRules() should not throw an exception.'); |
||
983 | |||
984 | return; |
||
985 | } |
||
986 | |||
987 | self::assertInstanceOf(AuthenticationFailure::class, $exception); |
||
988 | self::assertSame($expected, $exception->failureType); |
||
989 | } |
||
990 | |||
991 | /** @return mixed[] */ |
||
992 | public static function checkRulesProvider(): array |
||
993 | { |
||
994 | return [ |
||
995 | 'nopass-ok' => ['testUser', '', '1.2.3.4', true, true, [], ''], |
||
996 | 'nopass' => ['testUser', '', '1.2.3.4', true, false, [], AuthenticationFailure::EMPTY_DENIED], |
||
997 | 'root-ok' => ['root', 'root', '1.2.3.4', true, true, [], ''], |
||
998 | 'root' => ['root', 'root', '1.2.3.4', false, true, [], AuthenticationFailure::ROOT_DENIED], |
||
999 | 'rules-deny-allow-ok' => [ |
||
1000 | 'root', |
||
1001 | 'root', |
||
1002 | '1.2.3.4', |
||
1003 | true, |
||
1004 | true, |
||
1005 | ['order' => 'deny,allow', 'rules' => ['allow root 1.2.3.4', 'deny % from all']], |
||
1006 | '', |
||
1007 | ], |
||
1008 | 'rules-deny-allow-reject' => [ |
||
1009 | 'user', |
||
1010 | 'root', |
||
1011 | '1.2.3.4', |
||
1012 | true, |
||
1013 | true, |
||
1014 | ['order' => 'deny,allow', 'rules' => ['allow root 1.2.3.4', 'deny % from all']], |
||
1015 | AuthenticationFailure::ALLOW_DENIED, |
||
1016 | ], |
||
1017 | 'rules-allow-deny-ok' => [ |
||
1018 | 'root', |
||
1019 | 'root', |
||
1020 | '1.2.3.4', |
||
1021 | true, |
||
1022 | true, |
||
1023 | ['order' => 'allow,deny', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1024 | '', |
||
1025 | ], |
||
1026 | 'rules-allow-deny-reject' => [ |
||
1027 | 'user', |
||
1028 | 'root', |
||
1029 | '1.2.3.4', |
||
1030 | true, |
||
1031 | true, |
||
1032 | ['order' => 'allow,deny', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1033 | AuthenticationFailure::ALLOW_DENIED, |
||
1034 | ], |
||
1035 | 'rules-explicit-ok' => [ |
||
1036 | 'root', |
||
1037 | 'root', |
||
1038 | '1.2.3.4', |
||
1039 | true, |
||
1040 | true, |
||
1041 | ['order' => 'explicit', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1042 | '', |
||
1043 | ], |
||
1044 | 'rules-explicit-reject' => [ |
||
1045 | 'user', |
||
1046 | 'root', |
||
1047 | '1.2.3.4', |
||
1048 | true, |
||
1049 | true, |
||
1050 | ['order' => 'explicit', 'rules' => ['deny user from all', 'allow root 1.2.3.4']], |
||
1051 | AuthenticationFailure::ALLOW_DENIED, |
||
1052 | ], |
||
1053 | ]; |
||
1054 | } |
||
1055 | } |
||
1056 |