API: Fix fetching login token from action=query&meta=tokens on private wikis
authorBrad Jorsch <bjorsch@wikimedia.org>
Fri, 14 Feb 2020 20:22:36 +0000 (15:22 -0500)
committerAnomie <bjorsch@wikimedia.org>
Mon, 16 Mar 2020 17:57:21 +0000 (17:57 +0000)
Accidentally broken by I991809acf.

Also added a test that should hopefully prevent this from accidentally
being broken again.

Bug: T245149
Change-Id: Ia7985397db50efe8af81f643f2a0a89d0ece179e
(cherry picked from commit e0f3a29349ffd8a3be14cec164c1e0f719e0e74b)

includes/api/ApiQuery.php
tests/phpunit/includes/api/query/ApiQueryTest.php

index a7ff729..9e937f5 100644 (file)
@@ -509,15 +509,14 @@ class ApiQuery extends ApiBase {
                // parameters either. We do allow the 'rawcontinue' and 'indexpageids'
                // parameters since frameworks might add these unconditionally and they
                // can't expose anything here.
+               $allowedParams = [ 'rawcontinue' => 1, 'indexpageids' => 1 ];
                $this->mParams = $this->extractRequestParams();
-               $params = array_filter(
-                       array_diff_key(
-                               $this->mParams + $this->getPageSet()->extractRequestParams(),
-                               [ 'rawcontinue' => 1, 'indexpageids' => 1 ]
-                       )
-               );
-               if ( array_keys( $params ) !== [ 'meta' ] ) {
-                       return true;
+               $request = $this->getRequest();
+               foreach ( $this->mParams + $this->getPageSet()->extractRequestParams() as $param => $value ) {
+                       $needed = $param === 'meta';
+                       if ( !isset( $allowedParams[$param] ) && $request->getCheck( $param ) !== $needed ) {
+                               return true;
+                       }
                }
 
                // Ask each module if it requires read mode. Any true => this returns
index 20bd855..bdbd64c 100644 (file)
@@ -172,4 +172,48 @@ class ApiQueryTest extends ApiTestCase {
                // This response field contains an XML document even if no pages were exported
                $this->assertNotContains( $title->getPrefixedText(), $data[0]['query']['export'] );
        }
+
+       public function testIsReadMode() {
+               $api = new ApiMain(
+                       new FauxRequest( [ 'action' => 'query', 'meta' => 'tokens', 'type' => 'login' ] )
+               );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $this->assertFalse( $queryApi->isReadMode(),
+                       'isReadMode() => false when meta=tokens is the only module' );
+
+               $api = new ApiMain( new FauxRequest( [
+                       'action' => 'query', 'meta' => 'tokens', 'type' => 'login', 'rawcontinue' => 1,
+                       'indexpageids' => 1
+               ] )
+               );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $this->assertFalse( $queryApi->isReadMode(),
+                       'rawcontinue and indexpageids are also allowed' );
+
+               $api = new ApiMain(
+                       new FauxRequest( [ 'action' => 'query', 'meta' => 'tokens|siteinfo', 'type' => 'login' ] )
+               );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $this->assertTrue( $queryApi->isReadMode(),
+                       'isReadMode() => true when other meta modules are present' );
+
+               $api = new ApiMain( new FauxRequest( [
+                       'action' => 'query', 'meta' => 'tokens', 'type' => 'login', 'list' => 'allpages'
+               ] ) );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $this->assertTrue( $queryApi->isReadMode(),
+                       'isReadMode() => true when other modules are present' );
+
+               $api = new ApiMain( new FauxRequest( [
+                       'action' => 'query', 'meta' => 'tokens', 'type' => 'login', 'titles' => 'Foo'
+               ] ) );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $this->assertTrue( $queryApi->isReadMode(),
+                       'isReadMode() => true when other ApiQuery parameters are present' );
+
+               $api = new ApiMain( new FauxRequest( [ 'action' => 'query' ] ) );
+               $queryApi = new ApiQuery( $api, 'query' );
+               $this->assertTrue( $queryApi->isReadMode(),
+                       'isReadMode() => true when no modules are requested' );
+       }
 }