From 21fd7af97e4bb362c92521b38c0987c3c7d71344 Mon Sep 17 00:00:00 2001 From: Roan Kattouw Date: Fri, 6 Mar 2009 13:49:44 +0000 Subject: [PATCH] * API: (bug 17774) API pretends action=query doesn't exist for users without read rights * Instead of hiding read-restricted modules, throw an error when a user without read rights tries to use them * Do the same for write modules when $wgEnableWriteAPI is false * Indicate whether a module needs read or write rights in action=help and action=paraminfo * BREAKING CHANGE: action=purge now requires write rights and, for anonymous users, a POST request --- RELEASE-NOTES | 6 +++++ includes/api/ApiBase.php | 23 +++++++++++++--- includes/api/ApiBlock.php | 5 +++- includes/api/ApiDelete.php | 5 +++- includes/api/ApiDisabled.php | 4 +++ includes/api/ApiEditPage.php | 6 +++-- includes/api/ApiEmailUser.php | 9 ++++--- includes/api/ApiHelp.php | 4 +++ includes/api/ApiImport.php | 5 +++- includes/api/ApiLogin.php | 4 +++ includes/api/ApiLogout.php | 4 +++ includes/api/ApiMain.php | 51 +++++++++++++---------------------- includes/api/ApiMove.php | 5 +++- includes/api/ApiParamInfo.php | 10 +++++++ includes/api/ApiPatrol.php | 5 +++- includes/api/ApiProtect.php | 5 +++- includes/api/ApiPurge.php | 9 +++++++ includes/api/ApiRollback.php | 5 +++- includes/api/ApiUnblock.php | 5 +++- includes/api/ApiUndelete.php | 5 +++- includes/api/ApiWatch.php | 5 +++- 21 files changed, 129 insertions(+), 51 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 501d4686b7..970fbb5ac5 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -283,6 +283,12 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN conversion rules * (bug 17795) Don't report views count on meta=siteinfo if $wgDisableCounters is set +* (bug 17774) Don't hide read-restricted modules like action=query from users + without read rights, but throw an error when they try to use them. +* Don't hide write modules when $wgEnableWriteAPI is false, but throw an error + when someone tries to use them +* BREAKING CHANGE: action=purge requires write rights and, for anonymous users, + a POST request === Languages updated in 1.15 === diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index ad2b4e497e..4ab5f251c8 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -218,8 +218,15 @@ abstract class ApiBase { ); $msg = $lnPrfx . implode($lnPrfx, $msg) . "\n"; + if ($this->isReadMode()) + $msg .= "\nThis module requires read rights."; + if ($this->isWriteMode()) + $msg .= "\nThis module requires write rights."; if ($this->mustBePosted()) - $msg .= "\nThis module only accepts POST requests.\n"; + $msg .= "\nThis module only accepts POST requests."; + if ($this->isReadMode() || $this->isWriteMode() || + $this->mustBePosted()) + $msg .= "\n"; // Parameters $paramsMsg = $this->makeHelpMsgParameters(); @@ -758,6 +765,9 @@ abstract class ApiBase { 'movenotallowedfile' => array('code' => 'cantmovefile', 'info' => "You don't have permission to move files"), // API-specific messages + 'readrequired' => array('code' => 'readapidenied', 'info' => "You need read permission to use this module"), + 'writedisabled' => array('code' => 'noapiwrite', 'info' => "Editing of this wiki through the API is disabled. Make sure the \$wgEnableWriteAPI=true; statement is included in the wiki's LocalSettings.php file"), + 'writerequired' => array('code' => 'writeapidenied', 'info' => "You're not allowed to edit this wiki through the API"), 'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"), 'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"), 'nosuchpageid' => array('code' => 'nosuchpageid', 'info' => "There is no page with ID \$1"), @@ -855,10 +865,17 @@ abstract class ApiBase { } /** - * Indicates if this module requires edit mode + * Indicates whether this module requires read rights * @return bool */ - public function isEditMode() { + public function isReadMode() { + return true; + } + /** + * Indicates whether this module requires write mode + * @return bool + */ + public function isWriteMode() { return false; } diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php index 7d8b0877ae..fc77f2a057 100644 --- a/includes/api/ApiBlock.php +++ b/includes/api/ApiBlock.php @@ -50,7 +50,6 @@ class ApiBlock extends ApiBase { */ public function execute() { global $wgUser, $wgBlockAllowsUTEdit; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); if($params['gettoken']) @@ -115,6 +114,10 @@ class ApiBlock extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'user' => null, diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php index a892b1c86f..a9230210c7 100644 --- a/includes/api/ApiDelete.php +++ b/includes/api/ApiDelete.php @@ -49,7 +49,6 @@ class ApiDelete extends ApiBase { */ public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); $this->requireOnlyOneParameter($params, 'title', 'pageid'); @@ -178,6 +177,10 @@ class ApiDelete extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'title' => null, diff --git a/includes/api/ApiDisabled.php b/includes/api/ApiDisabled.php index 3136384d8e..c4cd621bb4 100644 --- a/includes/api/ApiDisabled.php +++ b/includes/api/ApiDisabled.php @@ -48,6 +48,10 @@ class ApiDisabled extends ApiBase { $this->dieUsage("The ``{$this->getModuleName()}'' module has been disabled.", 'moduledisabled'); } + public function isReadMode() { + return false; + } + public function getAllowedParams() { return array (); } diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index 8fc3f15dd4..a66006b2c1 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -43,8 +43,6 @@ class ApiEditPage extends ApiBase { public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); - $params = $this->extractRequestParams(); if(is_null($params['title'])) $this->dieUsageMsg(array('missingparam', 'title')); @@ -275,6 +273,10 @@ class ApiEditPage extends ApiBase { return true; } + public function isWriteMode() { + return true; + } + protected function getDescription() { return 'Create and edit pages.'; } diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php index 5548b18432..e1fe85e173 100644 --- a/includes/api/ApiEmailUser.php +++ b/includes/api/ApiEmailUser.php @@ -39,14 +39,11 @@ class ApiEmailUser extends ApiBase { public function execute() { global $wgUser; - // Check whether email is enabled if ( !EmailUserForm::userEmailEnabled() ) $this->dieUsageMsg( array( 'usermaildisabled' ) ); - - $this->getMain()->requestWriteMode(); + $params = $this->extractRequestParams(); - // Check required parameters if ( !isset( $params['target'] ) ) $this->dieUsageMsg( array( 'missingparam', 'target' ) ); @@ -79,6 +76,10 @@ class ApiEmailUser extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'target' => null, diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index 50e33feaad..982bf4d5ea 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -50,6 +50,10 @@ class ApiHelp extends ApiBase { return false; } + public function isReadMode() { + return false; + } + public function getDescription() { return array ( 'Display this help screen.' diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php index ef2f51eacd..cba0c566f7 100644 --- a/includes/api/ApiImport.php +++ b/includes/api/ApiImport.php @@ -41,7 +41,6 @@ class ApiImport extends ApiBase { public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); if(!$wgUser->isAllowed('import')) $this->dieUsageMsg(array('cantimport')); $params = $this->extractRequestParams(); @@ -101,6 +100,10 @@ class ApiImport extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { global $wgImportSources; return array ( diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php index b197c20189..66ff8dfbd4 100644 --- a/includes/api/ApiLogin.php +++ b/includes/api/ApiLogin.php @@ -125,6 +125,10 @@ class ApiLogin extends ApiBase { public function mustBePosted() { return true; } + public function isReadMode() { + return false; + } + public function getAllowedParams() { return array ( 'name' => null, diff --git a/includes/api/ApiLogout.php b/includes/api/ApiLogout.php index 000409e73e..1fb9c31899 100644 --- a/includes/api/ApiLogout.php +++ b/includes/api/ApiLogout.php @@ -50,6 +50,10 @@ class ApiLogout extends ApiBase { wfRunHooks( 'UserLogoutComplete', array(&$wgUser, &$injected_html, $oldName) ); } + public function isReadMode() { + return false; + } + public function getAllowedParams() { return array (); } diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index a2b981430a..3fa0d72f2b 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -65,10 +65,9 @@ class ApiMain extends ApiBase { 'feedwatchlist' => 'ApiFeedWatchlist', 'help' => 'ApiHelp', 'paraminfo' => 'ApiParamInfo', - 'purge' => 'ApiPurge', - ); - private static $WriteModules = array ( + // Write modules + 'purge' => 'ApiPurge', 'rollback' => 'ApiRollback', 'delete' => 'ApiDelete', 'undelete' => 'ApiUndelete', @@ -150,20 +149,10 @@ class ApiMain extends ApiBase { wfDebug( "API: stripping user credentials for JSON callback\n" ); $wgUser = new User(); } - - if (!$wgUser->isAllowed('read')) { - self::$Modules = array( - 'login' => self::$Modules['login'], - 'logout' => self::$Modules['logout'], - 'help' => self::$Modules['help'], - ); - } } - global $wgAPIModules, $wgEnableWriteAPI; // extension modules + global $wgAPIModules; // extension modules $this->mModules = $wgAPIModules + self :: $Modules; - if($wgEnableWriteAPI) - $this->mModules += self::$WriteModules; $this->mModuleNames = array_keys($this->mModules); $this->mFormats = self :: $Formats; @@ -200,24 +189,6 @@ class ApiMain extends ApiBase { return $this->mResult; } - /** - * This method will simply cause an error if the write mode was disabled - * or if the current user doesn't have the right to use it - */ - public function requestWriteMode() { - global $wgUser; - if (!$this->mEnableWrite) - $this->dieUsage('Editing of this wiki through the API' . - ' is disabled. Make sure the $wgEnableWriteAPI=true; ' . - 'statement is included in the wiki\'s ' . - 'LocalSettings.php file', 'noapiwrite'); - if (!$wgUser->isAllowed('writeapi')) - $this->dieUsage('You\'re not allowed to edit this ' . - 'wiki through the API', 'writeapidenied'); - if (wfReadOnly()) - $this->dieUsageMsg(array('readonlytext')); - } - /** * Set how long the response should be cached. */ @@ -410,6 +381,18 @@ class ApiMain extends ApiBase { } } + global $wgUser; + if ($module->isReadMode() && !$wgUser->isAllowed('read')) + $this->dieUsageMsg(array('readrequired')); + if ($module->isWriteMode()) { + if (!$this->mEnableWrite) + $this->dieUsageMsg(array('writedisabled')); + if (!$wgUser->isAllowed('writeapi')) + $this->dieUsageMsg(array('writerequired')); + if (wfReadOnly()) + $this->dieUsageMsg(array('readonlytext')); + } + if (!$this->mInternalMode) { // Ignore mustBePosted() for internal calls if($module->mustBePosted() && !$this->mRequest->wasPosted()) @@ -458,6 +441,10 @@ class ApiMain extends ApiBase { $printer->closePrinter(); $printer->profileOut(); } + + public function isReadMode() { + return false; + } /** * See ApiBase for description. diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php index c3a5e7dbec..975a8ba129 100644 --- a/includes/api/ApiMove.php +++ b/includes/api/ApiMove.php @@ -39,7 +39,6 @@ class ApiMove extends ApiBase { public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); if(is_null($params['reason'])) $params['reason'] = ''; @@ -155,6 +154,10 @@ class ApiMove extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'from' => null, diff --git a/includes/api/ApiParamInfo.php b/includes/api/ApiParamInfo.php index 7bc73b9cc6..e8f7b2a319 100644 --- a/includes/api/ApiParamInfo.php +++ b/includes/api/ApiParamInfo.php @@ -93,6 +93,12 @@ class ApiParamInfo extends ApiBase { $retval['classname'] = get_class($obj); $retval['description'] = (is_array($obj->getDescription()) ? implode("\n", $obj->getDescription()) : $obj->getDescription()); $retval['prefix'] = $obj->getModulePrefix(); + if($obj->isReadMode()) + $retval['readrights'] = ''; + if($obj->isWriteMode()) + $retval['writerights'] = ''; + if($obj->mustBePosted()) + $retval['mustbeposted'] = ''; $allowedParams = $obj->getFinalParams(); if(!is_array($allowedParams)) return $retval; @@ -147,6 +153,10 @@ class ApiParamInfo extends ApiBase { return $retval; } + public function isReadMode() { + return false; + } + public function getAllowedParams() { return array ( 'modules' => array( diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php index 5949f00805..dd57fd5704 100644 --- a/includes/api/ApiPatrol.php +++ b/includes/api/ApiPatrol.php @@ -42,7 +42,6 @@ class ApiPatrol extends ApiBase { */ public function execute() { global $wgUser, $wgUseRCPatrol, $wgUseNPPatrol; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); if(!isset($params['token'])) @@ -65,6 +64,10 @@ class ApiPatrol extends ApiBase { $this->getResult()->addValue(null, $this->getModuleName(), $result); } + public function getWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'token' => null, diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php index 6bae2fcb0b..66b284e92b 100644 --- a/includes/api/ApiProtect.php +++ b/includes/api/ApiProtect.php @@ -38,7 +38,6 @@ class ApiProtect extends ApiBase { public function execute() { global $wgUser, $wgRestrictionTypes, $wgRestrictionLevels; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); $titleObj = NULL; @@ -127,6 +126,10 @@ class ApiProtect extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'title' => null, diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php index a0c0808576..e2d00087a3 100644 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@ -74,6 +74,15 @@ class ApiPurge extends ApiBase { $this->getResult()->addValue(null, $this->getModuleName(), $result); } + public function mustBePosted() { + global $wgUser; + return $wgUser->isAnon(); + } + + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'titles' => array( diff --git a/includes/api/ApiRollback.php b/includes/api/ApiRollback.php index cb0486fd50..0ff602dc0e 100644 --- a/includes/api/ApiRollback.php +++ b/includes/api/ApiRollback.php @@ -37,7 +37,6 @@ class ApiRollback extends ApiBase { } public function execute() { - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); $titleObj = NULL; @@ -84,6 +83,10 @@ class ApiRollback extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'title' => null, diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php index b8568bc960..b36b3b4bdd 100644 --- a/includes/api/ApiUnblock.php +++ b/includes/api/ApiUnblock.php @@ -44,7 +44,6 @@ class ApiUnblock extends ApiBase { */ public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); if($params['gettoken']) @@ -80,6 +79,10 @@ class ApiUnblock extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'id' => null, diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php index 9bb2efb992..9a1e9276dc 100644 --- a/includes/api/ApiUndelete.php +++ b/includes/api/ApiUndelete.php @@ -38,7 +38,6 @@ class ApiUndelete extends ApiBase { public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); $params = $this->extractRequestParams(); $titleObj = NULL; @@ -86,6 +85,10 @@ class ApiUndelete extends ApiBase { public function mustBePosted() { return true; } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'title' => null, diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index 56839129dd..94569b6a21 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -41,7 +41,6 @@ class ApiWatch extends ApiBase { public function execute() { global $wgUser; - $this->getMain()->requestWriteMode(); if(!$wgUser->isLoggedIn()) $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin'); $params = $this->extractRequestParams(); @@ -65,6 +64,10 @@ class ApiWatch extends ApiBase { $this->getResult()->addValue(null, $this->getModuleName(), $res); } + public function isWriteMode() { + return true; + } + public function getAllowedParams() { return array ( 'title' => null, -- 2.20.1