diff options
5 files changed, 85 insertions, 7 deletions
diff --git a/includes/Rest/Handler/Helper/PageContentHelper.php b/includes/Rest/Handler/Helper/PageContentHelper.php index b9e3d7d164ef..7437de25c70e 100644 --- a/includes/Rest/Handler/Helper/PageContentHelper.php +++ b/includes/Rest/Handler/Helper/PageContentHelper.php @@ -334,13 +334,27 @@ class PageContentHelper { ], 'redirect' => [ Handler::PARAM_SOURCE => 'query', - ParamValidator::PARAM_TYPE => [ 'no' ], + ParamValidator::PARAM_TYPE => 'boolean', ParamValidator::PARAM_REQUIRED => false, + ParamValidator::PARAM_DEFAULT => true, ] ]; } /** + * Whether the handler is allowed to follow redirects, according to the + * request parameters. + * + * Handlers that can follow wiki redirects can use this to give clients + * control over the redirect handling behavior. + * + * @return bool + */ + public function getRedirectsAllowed(): bool { + return $this->parameters['redirect'] ?? true; + } + + /** * Sets the 'Cache-Control' header no more then provided $expiry. * @param ResponseInterface $response * @param int|null $expiry diff --git a/includes/Rest/Handler/PageHTMLHandler.php b/includes/Rest/Handler/PageHTMLHandler.php index 1e3ad4541634..bed9d9a1504b 100644 --- a/includes/Rest/Handler/PageHTMLHandler.php +++ b/includes/Rest/Handler/PageHTMLHandler.php @@ -77,13 +77,8 @@ class PageHTMLHandler extends SimpleHandler { public function run(): Response { $this->contentHelper->checkAccessPermission(); $page = $this->contentHelper->getPageIdentity(); - $params = $this->getRequest()->getQueryParams(); - if ( array_key_exists( 'redirect', $params ) ) { - $followWikiRedirects = $params['redirect'] !== 'no'; - } else { - $followWikiRedirects = true; - } + $followWikiRedirects = $this->contentHelper->getRedirectsAllowed(); // The call to $this->contentHelper->getPage() should not return null if // $this->contentHelper->checkAccess() did not throw. diff --git a/tests/api-testing/REST/content.v1/Page.js b/tests/api-testing/REST/content.v1/Page.js index 49a103fff2f0..58b82ab9304d 100644 --- a/tests/api-testing/REST/content.v1/Page.js +++ b/tests/api-testing/REST/content.v1/Page.js @@ -251,6 +251,16 @@ describe( 'Page Source', () => { assert.match( headers[ 'content-type' ], /^text\/html/ ); } ); + it( 'Bypass wiki redirects with query param redirect=false', async () => { + const redirectPageDbkey = utils.dbkey( redirectPage ); + const { status, text, headers } = await client.get( + `/page/${ redirectPageDbkey }/html`, + { redirect: 'false' } + ); + assert.deepEqual( status, 200, text ); + assert.match( headers[ 'content-type' ], /^text\/html/ ); + } ); + it( 'Bypass variant redirects with query param redirect=no', async () => { const agepayDbkey = utils.dbkey( agepay ); const { status, headers } = await client.get( diff --git a/tests/phpunit/integration/includes/Rest/Handler/Helper/PageContentHelperTest.php b/tests/phpunit/integration/includes/Rest/Handler/Helper/PageContentHelperTest.php index 2f095a95ba08..e840e72dadcb 100644 --- a/tests/phpunit/integration/includes/Rest/Handler/Helper/PageContentHelperTest.php +++ b/tests/phpunit/integration/includes/Rest/Handler/Helper/PageContentHelperTest.php @@ -296,6 +296,28 @@ class PageContentHelperTest extends MediaWikiIntegrationTestCase { $helper->checkAccess(); } + public static function provideRedirectsAllowed() { + yield [ [], true ]; + yield [ [ 'redirect' => true ], true ]; + yield [ [ 'redirect' => false ], false ]; + } + + /** + * @dataProvider provideRedirectsAllowed + */ + public function testRedirectsAllowed( array $params, bool $allowRedirect ) { + $page = $this->getNonexistingTestPage( + Title::makeTitle( NS_MEDIAWIKI, 'Logouttext' ) + ); + $title = $page->getTitle(); + $helper = $this->newHelper( + $params + [ 'title' => $title->getPrefixedDBkey() ], + $this->mockAnonUltimateAuthority() + ); + + $this->assertSame( $allowRedirect, $helper->getRedirectsAllowed() ); + } + public function testParameterSettings() { $helper = $this->newHelper(); $settings = $helper->getParamSettings(); diff --git a/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php b/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php index 4d5c20b25e60..1223778fe8c4 100644 --- a/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php +++ b/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php @@ -7,6 +7,7 @@ use MediaWiki\Deferred\DeferredUpdates; use MediaWiki\Hook\ParserLogLinterDataHook; use MediaWiki\MainConfigNames; use MediaWiki\Rest\Handler\PageHTMLHandler; +use MediaWiki\Rest\HttpException; use MediaWiki\Rest\LocalizedHttpException; use MediaWiki\Rest\RequestData; use MediaWiki\Title\Title; @@ -162,6 +163,42 @@ class PageHTMLHandlerTest extends MediaWikiIntegrationTestCase { $this->assertStringContainsString( self::HTML, $htmlResponse ); } + public static function provideWikiRedirect() { + yield 'follow wiki redirects per default' => [ [], 307 ]; + yield 'bad redirect param' => [ [ 'redirect' => 'wrong' ], 400 ]; + yield 'redirect=no' => [ [ 'redirect' => 'no' ], 200 ]; + yield 'redirect=false' => [ [ 'redirect' => 'false' ], 200 ]; + yield 'redirect=true' => [ [ 'redirect' => 'true' ], 307 ]; + } + + /** + * @dataProvider provideWikiRedirect + */ + public function testWikiRedirect( $params, $expectedStatus ) { + $redirect = $this->getExistingTestPage( 'HtmlEndpointTestPage/redirect' ); + $page = $this->getExistingTestPage( 'HtmlEndpointTestPage/target' ); + + $this->editPage( $redirect, "#REDIRECT [[{$page->getTitle()->getPrefixedDBkey()}]]" ); + + $request = new RequestData( + [ + 'pathParams' => [ 'title' => $redirect->getTitle()->getPrefixedText() ], + 'queryParams' => $params + ] + ); + + try { + $handler = $this->newHandler(); + $response = $this->executeHandler( $handler, $request, [ + 'format' => 'html' + ] ); + + $this->assertSame( $expectedStatus, $response->getStatusCode() ); + } catch ( HttpException $ex ) { + $this->assertSame( $expectedStatus, $ex->getCode() ); + } + } + public function testExecuteHtmlOnlyForSystemMessagePage() { $title = Title::newFromText( 'MediaWiki:Logouttext/de' ); $page = $this->getNonexistingTestPage( $title ); |