Skip to content

Commit 2dd24ef

Browse files
committed
Extract Application methods to their respective middleware
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
1 parent e0dae7b commit 2dd24ef

8 files changed

Lines changed: 249 additions & 222 deletions

File tree

libraries/classes/Application.php

Lines changed: 3 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
use PhpMyAdmin\Http\Handler\QueueRequestHandler;
1414
use PhpMyAdmin\Http\Response;
1515
use PhpMyAdmin\Http\ServerRequest;
16-
use PhpMyAdmin\Identifiers\DatabaseName;
17-
use PhpMyAdmin\Identifiers\TableName;
1816
use PhpMyAdmin\Middleware\Authentication;
1917
use PhpMyAdmin\Middleware\ConfigErrorAndPermissionChecking;
2018
use PhpMyAdmin\Middleware\ConfigLoading;
@@ -53,20 +51,9 @@
5351
use PhpMyAdmin\Routing\Routing;
5452
use Psr\Http\Message\ResponseInterface;
5553
use Psr\Http\Message\ServerRequestInterface;
56-
use Symfony\Component\DependencyInjection\ContainerInterface;
5754
use Throwable;
5855

59-
use function __;
60-
use function function_exists;
61-
use function hash_equals;
62-
use function is_array;
63-
use function is_scalar;
64-
use function session_id;
6556
use function sprintf;
66-
use function strlen;
67-
use function trigger_error;
68-
69-
use const E_USER_ERROR;
7057

7158
class Application
7259
{
@@ -93,7 +80,7 @@ public function run(bool $isSetupPage = false): void
9380
$requestHandler = new QueueRequestHandler(new ApplicationHandler($this));
9481
$requestHandler->add(new ErrorHandling($this->errorHandler));
9582
$requestHandler->add(new OutputBuffering());
96-
$requestHandler->add(new PhpExtensionsChecking($this, $this->template, $this->responseFactory));
83+
$requestHandler->add(new PhpExtensionsChecking($this->template, $this->responseFactory));
9784
$requestHandler->add(new ServerConfigurationChecking($this->template, $this->responseFactory));
9885
$requestHandler->add(new PhpSettingsConfiguration());
9986
$requestHandler->add(new RouteParsing());
@@ -107,8 +94,8 @@ public function run(bool $isSetupPage = false): void
10794
));
10895
$requestHandler->add(new EncryptedQueryParamsHandling());
10996
$requestHandler->add(new UrlParamsSetting($this->config));
110-
$requestHandler->add(new TokenRequestParamChecking($this));
111-
$requestHandler->add(new DatabaseAndTableSetting($this));
97+
$requestHandler->add(new TokenRequestParamChecking());
98+
$requestHandler->add(new DatabaseAndTableSetting());
11299
$requestHandler->add(new SqlQueryGlobalSetting());
113100
$requestHandler->add(new LanguageLoading());
114101
$requestHandler->add(new ConfigErrorAndPermissionChecking(
@@ -162,119 +149,4 @@ public function handle(ServerRequest $request): Response|null
162149
$this->responseFactory,
163150
);
164151
}
165-
166-
/**
167-
* Checks that required PHP extensions are there.
168-
*/
169-
public function checkRequiredPhpExtensions(): void
170-
{
171-
/**
172-
* Warning about mbstring.
173-
*/
174-
if (! function_exists('mb_detect_encoding')) {
175-
Core::warnMissingExtension('mbstring');
176-
}
177-
178-
/**
179-
* We really need this one!
180-
*/
181-
if (! function_exists('preg_replace')) {
182-
Core::warnMissingExtension('pcre', true);
183-
}
184-
185-
/**
186-
* JSON is required in several places.
187-
*/
188-
if (! function_exists('json_encode')) {
189-
Core::warnMissingExtension('json', true);
190-
}
191-
192-
/**
193-
* ctype is required for Twig.
194-
*/
195-
if (! function_exists('ctype_alpha')) {
196-
Core::warnMissingExtension('ctype', true);
197-
}
198-
199-
if (! function_exists('mysqli_connect')) {
200-
$moreInfo = sprintf(__('See %sour documentation%s for more information.'), '[doc@faqmysql]', '[/doc]');
201-
Core::warnMissingExtension('mysqli', true, $moreInfo);
202-
}
203-
204-
if (! function_exists('session_name')) {
205-
Core::warnMissingExtension('session', true);
206-
}
207-
208-
/**
209-
* hash is required for cookie authentication.
210-
*/
211-
if (function_exists('hash_hmac')) {
212-
return;
213-
}
214-
215-
Core::warnMissingExtension('hash', true);
216-
}
217-
218-
/**
219-
* Check whether user supplied token is valid, if not remove any possibly
220-
* dangerous stuff from request.
221-
*
222-
* Check for token mismatch only if the Request method is POST.
223-
* GET Requests would never have token and therefore checking
224-
* mis-match does not make sense.
225-
*/
226-
public function checkTokenRequestParam(): void
227-
{
228-
$GLOBALS['token_mismatch'] = true;
229-
$GLOBALS['token_provided'] = false;
230-
231-
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
232-
return;
233-
}
234-
235-
if (isset($_POST['token']) && is_scalar($_POST['token']) && strlen((string) $_POST['token']) > 0) {
236-
$GLOBALS['token_provided'] = true;
237-
$GLOBALS['token_mismatch'] = ! @hash_equals($_SESSION[' PMA_token '], (string) $_POST['token']);
238-
}
239-
240-
if (! $GLOBALS['token_mismatch']) {
241-
return;
242-
}
243-
244-
// Warn in case the mismatch is result of failed setting of session cookie
245-
if (isset($_POST['set_session']) && $_POST['set_session'] !== session_id()) {
246-
trigger_error(
247-
__(
248-
'Failed to set session cookie. Maybe you are using HTTP instead of HTTPS to access phpMyAdmin.',
249-
),
250-
E_USER_ERROR,
251-
);
252-
}
253-
254-
/**
255-
* We don't allow any POST operation parameters if the token is mismatched
256-
* or is not provided.
257-
*/
258-
$allowList = ['ajax_request'];
259-
Sanitize::removeRequestVars($allowList);
260-
}
261-
262-
public function setDatabaseAndTableFromRequest(ContainerInterface $container, ServerRequest $request): void
263-
{
264-
$GLOBALS['urlParams'] ??= null;
265-
266-
$db = DatabaseName::tryFrom($request->getParam('db'));
267-
$table = TableName::tryFrom($request->getParam('table'));
268-
269-
$GLOBALS['db'] = $db?->getName() ?? '';
270-
$GLOBALS['table'] = $table?->getName() ?? '';
271-
272-
if (! is_array($GLOBALS['urlParams'])) {
273-
$GLOBALS['urlParams'] = [];
274-
}
275-
276-
$GLOBALS['urlParams']['db'] = $GLOBALS['db'];
277-
$GLOBALS['urlParams']['table'] = $GLOBALS['table'];
278-
$container->setParameter('url_params', $GLOBALS['urlParams']);
279-
}
280152
}

libraries/classes/Middleware/DatabaseAndTableSetting.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,46 @@
44

55
namespace PhpMyAdmin\Middleware;
66

7-
use PhpMyAdmin\Application;
87
use PhpMyAdmin\Core;
98
use PhpMyAdmin\Http\ServerRequest;
9+
use PhpMyAdmin\Identifiers\DatabaseName;
10+
use PhpMyAdmin\Identifiers\TableName;
1011
use Psr\Http\Message\ResponseInterface;
1112
use Psr\Http\Message\ServerRequestInterface;
1213
use Psr\Http\Server\MiddlewareInterface;
1314
use Psr\Http\Server\RequestHandlerInterface;
15+
use Symfony\Component\DependencyInjection\ContainerInterface;
1416

1517
use function assert;
18+
use function is_array;
1619

1720
final class DatabaseAndTableSetting implements MiddlewareInterface
1821
{
19-
public function __construct(private readonly Application $application)
20-
{
21-
}
22-
2322
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
2423
{
2524
assert($request instanceof ServerRequest);
2625
$container = Core::getContainerBuilder();
27-
$this->application->setDatabaseAndTableFromRequest($container, $request);
26+
$this->setDatabaseAndTableFromRequest($container, $request);
2827

2928
return $handler->handle($request);
3029
}
30+
31+
private function setDatabaseAndTableFromRequest(ContainerInterface $container, ServerRequest $request): void
32+
{
33+
$GLOBALS['urlParams'] ??= null;
34+
35+
$db = DatabaseName::tryFrom($request->getParam('db'));
36+
$table = TableName::tryFrom($request->getParam('table'));
37+
38+
$GLOBALS['db'] = $db?->getName() ?? '';
39+
$GLOBALS['table'] = $table?->getName() ?? '';
40+
41+
if (! is_array($GLOBALS['urlParams'])) {
42+
$GLOBALS['urlParams'] = [];
43+
}
44+
45+
$GLOBALS['urlParams']['db'] = $GLOBALS['db'];
46+
$GLOBALS['urlParams']['table'] = $GLOBALS['table'];
47+
$container->setParameter('url_params', $GLOBALS['urlParams']);
48+
}
3149
}

libraries/classes/Middleware/PhpExtensionsChecking.php

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace PhpMyAdmin\Middleware;
66

77
use Fig\Http\Message\StatusCodeInterface;
8-
use PhpMyAdmin\Application;
8+
use PhpMyAdmin\Core;
99
use PhpMyAdmin\Exceptions\MissingExtensionException;
1010
use PhpMyAdmin\Http\Factory\ResponseFactory;
1111
use PhpMyAdmin\Template;
@@ -14,19 +14,20 @@
1414
use Psr\Http\Server\MiddlewareInterface;
1515
use Psr\Http\Server\RequestHandlerInterface;
1616

17+
use function __;
18+
use function function_exists;
19+
use function sprintf;
20+
1721
final class PhpExtensionsChecking implements MiddlewareInterface
1822
{
19-
public function __construct(
20-
private readonly Application $application,
21-
private readonly Template $template,
22-
private readonly ResponseFactory $responseFactory,
23-
) {
23+
public function __construct(private readonly Template $template, private readonly ResponseFactory $responseFactory)
24+
{
2425
}
2526

2627
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
2728
{
2829
try {
29-
$this->application->checkRequiredPhpExtensions();
30+
$this->checkRequiredPhpExtensions();
3031
} catch (MissingExtensionException $exception) {
3132
// Disables template caching because the cache directory is not known yet.
3233
$this->template->disableCache();
@@ -43,4 +44,56 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
4344

4445
return $handler->handle($request);
4546
}
47+
48+
/**
49+
* Checks that required PHP extensions are there.
50+
*/
51+
private function checkRequiredPhpExtensions(): void
52+
{
53+
/**
54+
* Warning about mbstring.
55+
*/
56+
if (! function_exists('mb_detect_encoding')) {
57+
Core::warnMissingExtension('mbstring');
58+
}
59+
60+
/**
61+
* We really need this one!
62+
*/
63+
if (! function_exists('preg_replace')) {
64+
Core::warnMissingExtension('pcre', true);
65+
}
66+
67+
/**
68+
* JSON is required in several places.
69+
*/
70+
if (! function_exists('json_encode')) {
71+
Core::warnMissingExtension('json', true);
72+
}
73+
74+
/**
75+
* ctype is required for Twig.
76+
*/
77+
if (! function_exists('ctype_alpha')) {
78+
Core::warnMissingExtension('ctype', true);
79+
}
80+
81+
if (! function_exists('mysqli_connect')) {
82+
$moreInfo = sprintf(__('See %sour documentation%s for more information.'), '[doc@faqmysql]', '[/doc]');
83+
Core::warnMissingExtension('mysqli', true, $moreInfo);
84+
}
85+
86+
if (! function_exists('session_name')) {
87+
Core::warnMissingExtension('session', true);
88+
}
89+
90+
/**
91+
* hash is required for cookie authentication.
92+
*/
93+
if (function_exists('hash_hmac')) {
94+
return;
95+
}
96+
97+
Core::warnMissingExtension('hash', true);
98+
}
4699
}

libraries/classes/Middleware/TokenRequestParamChecking.php

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,71 @@
44

55
namespace PhpMyAdmin\Middleware;
66

7-
use PhpMyAdmin\Application;
7+
use PhpMyAdmin\Sanitize;
88
use Psr\Http\Message\ResponseInterface;
99
use Psr\Http\Message\ServerRequestInterface;
1010
use Psr\Http\Server\MiddlewareInterface;
1111
use Psr\Http\Server\RequestHandlerInterface;
1212

13+
use function __;
14+
use function hash_equals;
15+
use function is_scalar;
16+
use function session_id;
17+
use function strlen;
18+
use function trigger_error;
19+
20+
use const E_USER_ERROR;
21+
22+
/**
23+
* Check whether user supplied token is valid, if not remove any possibly
24+
* dangerous stuff from request.
25+
*
26+
* Check for token mismatch only if the Request method is POST.
27+
* GET Requests would never have token and therefore checking
28+
* mismatch does not make sense.
29+
*/
1330
final class TokenRequestParamChecking implements MiddlewareInterface
1431
{
15-
public function __construct(private readonly Application $application)
32+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
1633
{
34+
$this->checkTokenRequestParam();
35+
36+
return $handler->handle($request);
1737
}
1838

19-
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
39+
public function checkTokenRequestParam(): void
2040
{
21-
$this->application->checkTokenRequestParam();
41+
$GLOBALS['token_mismatch'] = true;
42+
$GLOBALS['token_provided'] = false;
2243

23-
return $handler->handle($request);
44+
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
45+
return;
46+
}
47+
48+
if (isset($_POST['token']) && is_scalar($_POST['token']) && strlen((string) $_POST['token']) > 0) {
49+
$GLOBALS['token_provided'] = true;
50+
$GLOBALS['token_mismatch'] = ! @hash_equals($_SESSION[' PMA_token '], (string) $_POST['token']);
51+
}
52+
53+
if (! $GLOBALS['token_mismatch']) {
54+
return;
55+
}
56+
57+
// Warn in case the mismatch is result of failed setting of session cookie
58+
if (isset($_POST['set_session']) && $_POST['set_session'] !== session_id()) {
59+
trigger_error(
60+
__(
61+
'Failed to set session cookie. Maybe you are using HTTP instead of HTTPS to access phpMyAdmin.',
62+
),
63+
E_USER_ERROR,
64+
);
65+
}
66+
67+
/**
68+
* We don't allow any POST operation parameters if the token is mismatched
69+
* or is not provided.
70+
*/
71+
$allowList = ['ajax_request'];
72+
Sanitize::removeRequestVars($allowList);
2473
}
2574
}

0 commit comments

Comments
 (0)