Merge branch 'MDL-82814-main' of https://github.com/andrewnicols/moodle
This commit is contained in:
@@ -33,7 +33,7 @@ use Psr\Http\Server\RequestHandlerInterface;
|
||||
class moodle_bootstrap_middleware implements MiddlewareInterface {
|
||||
#[\Override]
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
|
||||
global $PAGE;
|
||||
global $CFG, $PAGE;
|
||||
|
||||
if (str_contains($request->getUri(), route_loader_interface::ROUTE_GROUP_API)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
@@ -58,7 +58,52 @@ class moodle_bootstrap_middleware implements MiddlewareInterface {
|
||||
$this->load_full_moodle();
|
||||
}
|
||||
|
||||
$PAGE->set_url((string) $request->getUri());
|
||||
// Set the URL for the page.
|
||||
// Normally in Moodle this is a largely hard-coded value with only the query string changing dynamically in the page.
|
||||
// However, in this instance, we are generating the URL dynamically because we are the request terminator
|
||||
// for a large number of requests at different endpoints.
|
||||
|
||||
// In basic cases this will just work, but there are some edge cases to consider - specficially were the site
|
||||
// is behind a reverse proxy and/or an SSL terminator.
|
||||
// In these cases the URL we generate from the ServerRequestInterface may be the _terminated_ URL,
|
||||
// and not the URL that was requested by the client.
|
||||
|
||||
// We need to generate the URL that the client requested, not the URL that the server received.
|
||||
$url = $request->getUri();
|
||||
|
||||
if (!empty($CFG->reverseproxy)) {
|
||||
// This site is behind a reverse proxy. The requested URI may have a different:
|
||||
// - scheme
|
||||
// - host
|
||||
// - port
|
||||
// to the URL that the client requested.
|
||||
|
||||
$url = $url
|
||||
// Start by setting the scheme and host to the wwwroot.
|
||||
->withScheme(parse_url($CFG->wwwroot, PHP_URL_SCHEME))
|
||||
->withHost(parse_url($CFG->wwwroot, PHP_URL_HOST))
|
||||
|
||||
// Update the URL to match the port of the wwwroot.
|
||||
// While it is highly unlikely that a wwwroot includes an explicit port, we should still handle it.
|
||||
->withPort(parse_url($CFG->wwwroot, PHP_URL_PORT));
|
||||
|
||||
}
|
||||
|
||||
if (!empty($CFG->sslproxy)) {
|
||||
// This site is behind an ssl terminating proxy. The requested URI may have a different:
|
||||
// - scheme
|
||||
// - port
|
||||
// to the URL that the client requested.
|
||||
$url = $url
|
||||
// The wwwroot must use the https scheme, but the terminating request may have been received using http.
|
||||
->withScheme('https')
|
||||
|
||||
// Update the URL to match the port of the wwwroot.
|
||||
// While it is highly unlikely that a wwwroot includes an explicit port, we should still handle it.
|
||||
->withPort(parse_url($CFG->wwwroot, PHP_URL_PORT));
|
||||
}
|
||||
|
||||
$PAGE->set_url((string) $url);
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
namespace core\router\middleware;
|
||||
|
||||
use core\di;
|
||||
use core\url;
|
||||
use core\tests\route_testcase;
|
||||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
use Slim\Exception\HttpNotFoundException;
|
||||
|
||||
/**
|
||||
* Tests for the Moodle Bootstrap middleware.
|
||||
@@ -30,21 +32,198 @@ use GuzzleHttp\Psr7\ServerRequest;
|
||||
* @covers \core\router\middleware\moodle_bootstrap_middleware
|
||||
*/
|
||||
final class moodle_bootstrap_middleware_test extends route_testcase {
|
||||
public function test_set_page_to_uri(): void {
|
||||
global $PAGE;
|
||||
/**
|
||||
* Test setting of the page URI based on the request URI.
|
||||
*
|
||||
* @dataProvider page_url_provider
|
||||
*
|
||||
* @param string $pattern The pattern to register with the app.
|
||||
* @param string $basepath The basepath of the wwwroot.
|
||||
* @param string $uri The URI to test.
|
||||
* @param array $cfg The configuration to use.
|
||||
* @param string|false $expected The expected page URI, or false if no page URI is expected.
|
||||
*/
|
||||
public function test_set_page_to_uri(
|
||||
string $pattern,
|
||||
string $basepath,
|
||||
string $uri,
|
||||
array $cfg,
|
||||
string|false $expected
|
||||
): void {
|
||||
global $CFG, $PAGE;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
foreach ($cfg as $key => $value) {
|
||||
$CFG->{$key} = $value;
|
||||
}
|
||||
|
||||
$app = $this->get_simple_app();
|
||||
$app->setBasePath($basepath);
|
||||
$app->add(di::get(moodle_bootstrap_middleware::class));
|
||||
$app->addRoutingMiddleware();
|
||||
|
||||
$app->map(['GET'], '/example', function ($request, $response) {
|
||||
return $response;
|
||||
});
|
||||
$app->map(
|
||||
methods: ['GET'],
|
||||
pattern: $pattern,
|
||||
callable: fn ($request, $response) => $response,
|
||||
);
|
||||
|
||||
// Handle the request.
|
||||
$request = new ServerRequest('GET', '/example');
|
||||
$app->handle($request);
|
||||
$request = new ServerRequest('GET', $uri);
|
||||
|
||||
$expect = new \moodle_url('/example');
|
||||
$this->assertEquals($expect->out(), $PAGE->url->out());
|
||||
if ($expected) {
|
||||
$app->handle($request);
|
||||
$expect = new url($expected);
|
||||
$this->assertEquals($expect->out(), $PAGE->url->out());
|
||||
} else {
|
||||
$this->expectException(HttpNotFoundException::class);
|
||||
$app->handle($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_set_page_to_uri.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function page_url_provider(): array {
|
||||
return [
|
||||
'A basic URI' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '',
|
||||
'uri' => '/example',
|
||||
'cfg' => [],
|
||||
'expected' => '/example',
|
||||
],
|
||||
'A basic URI including wwwroot' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/example/path',
|
||||
'uri' => "https://example.com/example/path/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/example/path',
|
||||
],
|
||||
'expected' => '/example',
|
||||
],
|
||||
'A basic URI with query parameters' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '',
|
||||
'uri' => "https://example.com/example?foo=bar&baz=qux",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com',
|
||||
],
|
||||
'expected' => "https://example.com/example?foo=bar&baz=qux",
|
||||
],
|
||||
'A request behind a terminating SSL proxy' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://example.com:443/moodle/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'sslproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com/moodle/example',
|
||||
],
|
||||
'A request behind a terminating SSL proxy with query parameters' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://example.com:443/moodle/example?foo=bar&baz=qux",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'sslproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com/moodle/example?foo=bar&baz=qux',
|
||||
],
|
||||
'A request behind a terminating SSL proxy and random port' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://example.com:1024/moodle/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'sslproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com/moodle/example',
|
||||
],
|
||||
'A request behind a terminating SSL proxy and random port where the wwwroot has an explicit port' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://example.com:1024/moodle/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com:8443/moodle',
|
||||
'sslproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com:8443/moodle/example',
|
||||
],
|
||||
'A request behind a terminating SSL proxy where the wwwroot has an explicity port and query parameters' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://example.com:1024/moodle/example?foo=bar&baz=qux",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com:8443/moodle',
|
||||
'sslproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com:8443/moodle/example?foo=bar&baz=qux',
|
||||
],
|
||||
'A request behind a SSL proxy proxy with different path is invalid' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://example.com:443/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'sslproxy' => true,
|
||||
],
|
||||
'expected' => false,
|
||||
],
|
||||
'A request behind a reverse proxy' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://172.30.10.101:443/moodle/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'reverseproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com/moodle/example',
|
||||
],
|
||||
'A request behind a reverse proxy with query params' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://172.30.10.101:443/moodle/example?foo=bar&baz=qux",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'reverseproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com/moodle/example?foo=bar&baz=qux',
|
||||
],
|
||||
'A request behind a reverse proxy with different path is invalid' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://172.30.10.101:443/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com/moodle',
|
||||
'reverseproxy' => true,
|
||||
],
|
||||
'expected' => false,
|
||||
],
|
||||
'A request behind a reverse proxy where the wwwroot has an explicit port' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://172.30.10.101:443/moodle/example",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com:8443/moodle',
|
||||
'reverseproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com:8443/moodle/example',
|
||||
],
|
||||
'A request behind a reverse proxy where the wwwroot has an explicit port and parameters' => [
|
||||
'pattern' => '/example',
|
||||
'basepath' => '/moodle',
|
||||
'uri' => "http://172.30.10.101:443/moodle/example?foo=bar&baz=qux",
|
||||
'cfg' => [
|
||||
'wwwroot' => 'https://example.com:8443/moodle',
|
||||
'reverseproxy' => true,
|
||||
],
|
||||
'expected' => 'https://example.com:8443/moodle/example?foo=bar&baz=qux',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user