diff options
author | Petr Pchelko <ppchelko@wikimedia.org> | 2021-06-01 11:43:35 -0700 |
---|---|---|
committer | Petr Pchelko <ppchelko@wikimedia.org> | 2021-07-12 14:19:15 -0700 |
commit | 0d75fdb4f73ddbf4ad5f29e14258d58e507d55b4 (patch) | |
tree | 9e17f616f0b17370c219c3e4a339c0f3476155e7 | |
parent | 4e7ac57b46e5397f88d1de723bee345bf9d83e8f (diff) | |
download | mediawikicore-0d75fdb4f73ddbf4ad5f29e14258d58e507d55b4.tar.gz mediawikicore-0d75fdb4f73ddbf4ad5f29e14258d58e507d55b4.zip |
Use CsrfTokenSet as CSRF token source
Change-Id: I079d2c802d9b48d6abf7f37fa9ef7dafac631345
53 files changed, 303 insertions, 216 deletions
diff --git a/includes/EditPage.php b/includes/EditPage.php index d5399eb4b1d0..30b64d0b860b 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -55,6 +55,7 @@ use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionStore; use MediaWiki\Revision\RevisionStoreRecord; use MediaWiki\Revision\SlotRecord; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserIdentity; use MediaWiki\User\UserNameUtils; use MediaWiki\Watchlist\WatchlistManager; @@ -1624,9 +1625,7 @@ class EditPage implements IEditObject { * @internal */ public function tokenOk( &$request ) { - $token = $request->getVal( 'wpEditToken' ); - $user = $this->context->getUser(); - $this->mTokenOk = $user->matchEditToken( $token ); + $this->mTokenOk = ( new CsrfTokenSet( $request ) )->matchTokenField(); return $this->mTokenOk; } @@ -3490,7 +3489,10 @@ class EditPage implements IEditObject { */ $this->context->getOutput()->addHTML( "\n" . - Html::hidden( "wpEditToken", $this->context->getUser()->getEditToken() ) . + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->context->getCsrfTokenSet()->getToken()->toString() + ) . "\n" ); } diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php index 8d2828df87b0..3e7a6e235a71 100644 --- a/includes/FileDeleteForm.php +++ b/includes/FileDeleteForm.php @@ -25,6 +25,7 @@ use MediaWiki\Linker\LinkRenderer; use MediaWiki\MediaWikiServices; use MediaWiki\Permissions\PermissionStatus; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserIdentity; use MediaWiki\User\UserOptionsLookup; use MediaWiki\Watchlist\WatchlistManager; @@ -111,7 +112,6 @@ class FileDeleteForm { $request = $this->context->getRequest(); $this->oldimage = $request->getText( 'oldimage', '' ); - $token = $request->getText( 'wpEditToken' ); # Flag to hide all contents of the archived revisions $suppress = $request->getCheck( 'wpSuppress' ) && $this->context->getAuthority()->isAllowed( 'suppressrevision' ); @@ -130,7 +130,10 @@ class FileDeleteForm { } // Perform the deletion if appropriate - if ( $request->wasPosted() && $this->context->getUser()->matchEditToken( $token, $this->oldimage ) ) { + if ( $request->wasPosted() && + $this->context->getCsrfTokenSet() + ->matchTokenField( CsrfTokenSet::DEFAULT_FIELD_NAME, $this->oldimage ) + ) { $permissionStatus = PermissionStatus::newEmpty(); if ( !$this->context->getAuthority()->authorizeWrite( 'delete', $this->title, $permissionStatus @@ -431,8 +434,8 @@ class FileDeleteForm { $fieldset, new OOUI\HtmlSnippet( Html::hidden( - 'wpEditToken', - $this->context->getUser()->getEditToken( $this->oldimage ) + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->context->getCsrfTokenSet()->getToken( $this->oldimage )->toString() ) ) ); diff --git a/includes/Linker.php b/includes/Linker.php index 00be6c204c5c..2fc52084cfca 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -2184,7 +2184,7 @@ class Linker { $query = [ 'action' => 'rollback', 'from' => $revUserText, - 'token' => $context->getUser()->getEditToken( 'rollback' ), + 'token' => $context->getCsrfTokenSet()->getToken( 'rollback' )->toString(), ]; $attrs = [ diff --git a/includes/OutputPage.php b/includes/OutputPage.php index bfa6aea66f5f..4986dd0a4cc3 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -3487,7 +3487,7 @@ class OutputPage extends ContextSource { // Anons have predictable edit tokens return false; } - if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) { + if ( !$this->getCsrfTokenSet()->matchTokenField() ) { return false; } diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php index f1abbfca3ef0..ea237f8d0a17 100644 --- a/includes/ProtectionForm.php +++ b/includes/ProtectionForm.php @@ -28,6 +28,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Permissions\Authority; use MediaWiki\Permissions\PermissionManager; use MediaWiki\Permissions\PermissionStatus; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Watchlist\WatchlistManager; /** @@ -327,11 +328,10 @@ class ProtectionForm { return false; } - $token = $this->mRequest->getVal( 'wpEditToken' ); - $legacyUser = MediaWikiServices::getInstance() - ->getUserFactory() - ->newFromAuthority( $this->mPerformer ); - if ( !$legacyUser->matchEditToken( $token, [ 'protect', $this->mTitle->getPrefixedDBkey() ] ) ) { + if ( !$this->mContext->getCsrfTokenSet()->matchTokenField( + CsrfTokenSet::DEFAULT_FIELD_NAME, + [ 'protect', $this->mTitle->getPrefixedDBkey() ] + ) ) { $this->show( [ 'sessionfailure' ] ); return false; } @@ -585,13 +585,13 @@ class ProtectionForm { } if ( !$this->disabled ) { - $legacyUser = MediaWikiServices::getInstance() - ->getUserFactory() - ->newFromAuthority( $this->mPerformer ); - $fields['wpEditToken'] = [ - 'name' => 'wpEditToken', + $fields[CsrfTokenSet::DEFAULT_FIELD_NAME] = [ + 'name' => CsrfTokenSet::DEFAULT_FIELD_NAME, 'type' => 'hidden', - 'default' => $legacyUser->getEditToken( [ 'protect', $this->mTitle->getPrefixedDBkey() ] ), + 'default' => $this->mContext + ->getCsrfTokenSet() + ->getToken( [ 'protect', $this->mTitle->getPrefixedDBkey() ] ) + ->toString(), ]; } diff --git a/includes/Rest/Handler/EditHandler.php b/includes/Rest/Handler/EditHandler.php index 9c28ad210b2b..83a6a7ecf1a7 100644 --- a/includes/Rest/Handler/EditHandler.php +++ b/includes/Rest/Handler/EditHandler.php @@ -175,7 +175,7 @@ abstract class EditHandler extends ActionModuleBasedHandler { } // Since the session is safe against CSRF, just use a known-good token. - return $this->getUser()->getEditToken(); + return $this->getApiMain()->getCsrfTokenSet()->getToken()->toString(); } else { return $body['token'] ?? ''; } diff --git a/includes/actions/RollbackAction.php b/includes/actions/RollbackAction.php index 970a20350878..974321ce4a8a 100644 --- a/includes/actions/RollbackAction.php +++ b/includes/actions/RollbackAction.php @@ -115,7 +115,7 @@ class RollbackAction extends FormAction { ] ); } - if ( !$user->matchEditToken( $request->getVal( 'token' ), 'rollback' ) ) { + if ( !$this->getContext()->getCsrfTokenSet()->matchTokenField( 'token', 'rollback' ) ) { throw new ErrorPageError( 'sessionfailure-title', 'sessionfailure' ); } diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php index c1dc7644284e..4860a9549bed 100644 --- a/includes/actions/WatchAction.php +++ b/includes/actions/WatchAction.php @@ -23,6 +23,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Page\PageIdentity; use MediaWiki\Permissions\Authority; +use MediaWiki\Session\CsrfTokenSet; use Wikimedia\ParamValidator\TypeDef\ExpiryDef; /** @@ -332,7 +333,9 @@ class WatchAction extends FormAction { $action = 'watch'; } // This must match ApiWatch and ResourceLoaderUserOptionsModule - return $user->getEditToken( $action ); + return ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken( $action ) + ->toString(); } public function doesWrites() { diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 92b4d26e63c9..b9cccc0c3454 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -1097,11 +1097,7 @@ abstract class ApiBase extends ContextSource { } $webUiSalt = $this->getWebUITokenSalt( $params ); - if ( $webUiSalt !== null && $this->getUser()->matchEditToken( - $token, - $webUiSalt, - $this->getRequest() - ) ) { + if ( $webUiSalt !== null && $this->getCsrfTokenSet()->matchToken( $token, $webUiSalt ) ) { return true; } diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index a30285909598..1c95c958300f 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -26,6 +26,7 @@ use MediaWiki\Page\WikiPageFactory; use MediaWiki\Revision\RevisionLookup; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\SlotRecord; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserOptionsLookup; use MediaWiki\Watchlist\WatchlistManager; @@ -347,7 +348,7 @@ class ApiEditPage extends ApiBase { 'wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentModel, - 'wpEditToken' => $params['token'], + CsrfTokenSet::DEFAULT_FIELD_NAME => $params['token'], 'wpIgnoreBlankSummary' => true, 'wpIgnoreBlankArticle' => true, 'wpIgnoreSelfRedirect' => true, diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php index 043e2af43927..022b34cfbccc 100644 --- a/includes/api/ApiQueryDeletedrevs.php +++ b/includes/api/ApiQueryDeletedrevs.php @@ -191,7 +191,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase { if ( $fld_token ) { // Undelete tokens are identical for all pages, so we cache one here - $token = $user->getEditToken( '', $this->getMain()->getRequest() ); + $token = $this->getCsrfTokenSet()->getToken()->toString(); } $dir = $params['dir']; diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index 2efcfcce7e65..c6612a92b811 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -24,6 +24,7 @@ use MediaWiki\Languages\LanguageConverterFactory; use MediaWiki\Linker\LinkTarget; use MediaWiki\ParamValidator\TypeDef\TitleDef; use MediaWiki\Permissions\PermissionStatus; +use MediaWiki\Session\CsrfTokenSet; /** * A query module to show basic page information. @@ -208,7 +209,9 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['edit'] ) ) { - self::$cachedTokens['edit'] = $user->getEditToken(); + self::$cachedTokens['edit'] = ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken() + ->toString(); } return self::$cachedTokens['edit']; @@ -281,7 +284,9 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['email'] ) ) { - self::$cachedTokens['email'] = $user->getEditToken(); + self::$cachedTokens['email'] = ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken() + ->toString(); } return self::$cachedTokens['email']; @@ -299,7 +304,9 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['import'] ) ) { - self::$cachedTokens['import'] = $user->getEditToken(); + self::$cachedTokens['import'] = ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken() + ->toString(); } return self::$cachedTokens['import']; @@ -317,7 +324,9 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['watch'] ) ) { - self::$cachedTokens['watch'] = $user->getEditToken( 'watch' ); + self::$cachedTokens['watch'] = ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken( 'watch' ) + ->toString(); } return self::$cachedTokens['watch']; @@ -335,7 +344,9 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['options'] ) ) { - self::$cachedTokens['options'] = $user->getEditToken(); + self::$cachedTokens['options'] = ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken() + ->toString(); } return self::$cachedTokens['options']; diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php index bfcb833f07d3..37f16b1b704b 100644 --- a/includes/api/ApiQueryRecentChanges.php +++ b/includes/api/ApiQueryRecentChanges.php @@ -23,6 +23,7 @@ use MediaWiki\ParamValidator\TypeDef\UserDef; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\SlotRoleRegistry; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\NameTableAccessException; use MediaWiki\Storage\NameTableStore; @@ -126,7 +127,8 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase { static $cachedPatrolToken = null; if ( $cachedPatrolToken === null ) { - $cachedPatrolToken = $user->getEditToken( 'patrol' ); + $cachedPatrolToken = ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken( 'patrol' )->toString(); } return $cachedPatrolToken; diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index c843ecf6c705..ef9b4faa777a 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -25,6 +25,7 @@ use MediaWiki\ParamValidator\TypeDef\UserDef; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionStore; use MediaWiki\Revision\SlotRoleRegistry; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\NameTableAccessException; use MediaWiki\Storage\NameTableStore; @@ -119,7 +120,8 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase { return false; } - return $user->getEditToken( 'rollback' ); + return ( new CsrfTokenSet( $user->getRequest() ) ) + ->getToken( 'rollback' )->toString(); } protected function run( ApiPageSet $resultPageSet = null ) { diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php index b736da1ece7f..6dc1f212d913 100644 --- a/includes/api/ApiQueryUserInfo.php +++ b/includes/api/ApiQueryUserInfo.php @@ -211,7 +211,7 @@ class ApiQueryUserInfo extends ApiQueryBase { !$this->lacksSameOriginSecurity() && $this->getAuthority()->isAllowed( 'editmyoptions' ) ) { - $vals['preferencestoken'] = $user->getEditToken( '', $this->getMain()->getRequest() ); + $vals['preferencestoken'] = $this->getCsrfTokenSet()->getToken()->toString(); } if ( isset( $this->prop['editcount'] ) ) { diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php index 5dd07292416a..fecc56c9f55f 100644 --- a/includes/api/ApiQueryUsers.php +++ b/includes/api/ApiQueryUsers.php @@ -22,6 +22,7 @@ use MediaWiki\Auth\AuthManager; use MediaWiki\Block\DatabaseBlock; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserFactory; use MediaWiki\User\UserGroupManager; use MediaWiki\User\UserNameUtils; @@ -133,7 +134,9 @@ class ApiQueryUsers extends ApiQueryBase { public static function getUserrightsToken( User $actingUser, $targetUser ) { // Since the permissions check for userrights is non-trivial, // don't bother with it here - return $actingUser->getEditToken( $targetUser->getName() ); + return ( new CsrfTokenSet( $actingUser->getRequest() ) ) + ->getToken( $targetUser->getName() ) + ->toString(); } public function execute() { diff --git a/includes/context/DerivativeContext.php b/includes/context/DerivativeContext.php index 3a90335190a3..e40fd9b7fc7d 100644 --- a/includes/context/DerivativeContext.php +++ b/includes/context/DerivativeContext.php @@ -20,6 +20,7 @@ */ use MediaWiki\MediaWikiServices; use MediaWiki\Permissions\Authority; +use MediaWiki\Session\CsrfTokenSet; /** * An IContextSource implementation which will inherit context from another source @@ -287,4 +288,14 @@ class DerivativeContext extends ContextSource implements MutableContext { // phpcs:ignore MediaWiki.Usage.ExtendClassUsage.FunctionVarUsage return wfMessage( $key, ...$params )->setContext( $this ); } + + /** + * Get a repository to obtain and match CSRF tokens. + * + * @return CsrfTokenSet + * @since 1.37 + */ + public function getCsrfTokenSet() : CsrfTokenSet { + return new CsrfTokenSet( $this->getRequest() ); + } } diff --git a/includes/htmlform/HTMLForm.php b/includes/htmlform/HTMLForm.php index 7a74cbf8fb19..e0128aa7b168 100644 --- a/includes/htmlform/HTMLForm.php +++ b/includes/htmlform/HTMLForm.php @@ -24,6 +24,7 @@ use MediaWiki\HookContainer\ProtectedHookAccessorTrait; use MediaWiki\Linker\LinkTarget; use MediaWiki\Page\PageReference; +use MediaWiki\Session\CsrfTokenSet; /** * Object handling generic submission, CSRF protection, layout and @@ -592,12 +593,12 @@ class HTMLForm extends ContextSource { if ( $this->getMethod() !== 'post' ) { $tokenOkay = true; // no session check needed } elseif ( $this->getRequest()->wasPosted() ) { - $editToken = $this->getRequest()->getVal( 'wpEditToken' ); + $editToken = $this->getRequest()->getVal( CsrfTokenSet::DEFAULT_FIELD_NAME ); if ( $this->getUser()->isRegistered() || $editToken !== null ) { // Session tokens for logged-out users have no security value. // However, if the user gave one, check it in order to give a nice // "session expired" error instead of "permission denied" or such. - $tokenOkay = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt ); + $tokenOkay = $this->getCsrfTokenSet()->matchToken( $editToken, $this->mTokenSalt ); } else { $tokenOkay = true; } @@ -1194,9 +1195,9 @@ class HTMLForm extends ContextSource { } if ( $this->getMethod() === 'post' ) { $html .= Html::hidden( - 'wpEditToken', - $this->getUser()->getEditToken( $this->mTokenSalt ), - [ 'id' => 'wpEditToken' ] + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getCsrfTokenSet()->getToken( $this->mTokenSalt )->toString(), + [ 'id' => CsrfTokenSet::DEFAULT_FIELD_NAME ] ) . "\n"; $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n"; } diff --git a/includes/htmlform/HTMLFormField.php b/includes/htmlform/HTMLFormField.php index 43cb401330c6..bf5782ba1372 100644 --- a/includes/htmlform/HTMLFormField.php +++ b/includes/htmlform/HTMLFormField.php @@ -1,5 +1,7 @@ <?php +use MediaWiki\Session\CsrfTokenSet; + /** * The parent class to generate form fields. Any field type should * be a subclass of this. @@ -376,7 +378,8 @@ abstract class HTMLFormField { * @return bool */ protected function isSubmitAttempt( WebRequest $request ) { - return $request->getCheck( 'wpEditToken' ) || $request->getCheck( 'wpFormIdentifier' ); + return $request->getCheck( CsrfTokenSet::DEFAULT_FIELD_NAME ) || + $request->getCheck( 'wpFormIdentifier' ); } /** diff --git a/includes/htmlform/fields/HTMLFormFieldCloner.php b/includes/htmlform/fields/HTMLFormFieldCloner.php index 65a33cfcaef0..9d93729dfca6 100644 --- a/includes/htmlform/fields/HTMLFormFieldCloner.php +++ b/includes/htmlform/fields/HTMLFormFieldCloner.php @@ -1,5 +1,7 @@ <?php +use MediaWiki\Session\CsrfTokenSet; + /** * A container for HTMLFormFields that allows for multiple copies of the set of * fields to be displayed to and entered by the user. @@ -144,7 +146,9 @@ class HTMLFormFieldCloner extends HTMLFormField { public function loadDataFromRequest( $request ) { // It's possible that this might be posted with no fields. Detect that // by looking for an edit token. - if ( !$request->getCheck( 'wpEditToken' ) && $request->getArray( $this->mName ) === null ) { + if ( !$request->getCheck( CsrfTokenSet::DEFAULT_FIELD_NAME ) && + $request->getArray( $this->mName ) === null + ) { return $this->getDefault(); } @@ -160,8 +164,7 @@ class HTMLFormFieldCloner extends HTMLFormField { continue; } - // Add back in $request->getValues() so things that look for e.g. - // wpEditToken don't fail. + // Add back in $request->getValues() so things that look for e.g. wpEditToken don't fail. $data = $this->rekeyValuesArray( $key, $value ) + $request->getValues(); $fields = $this->createFieldsForKey( $key ); diff --git a/includes/page/Article.php b/includes/page/Article.php index 7a109e9d2ce4..203d34e088bb 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -30,6 +30,7 @@ use MediaWiki\Permissions\PermissionStatus; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionStore; use MediaWiki\Revision\SlotRecord; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserIdentity; use MediaWiki\User\UserNameUtils; use MediaWiki\Watchlist\WatchlistManager; @@ -1896,8 +1897,11 @@ class Article implements Page { $reason = $deleteReasonList; } - if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ), - [ 'delete', $this->getTitle()->getPrefixedText() ] ) + if ( $request->wasPosted() && + $this->getContext()->getCsrfTokenSet()->matchTokenField( + CsrfTokenSet::DEFAULT_FIELD_NAME, + [ 'delete', $this->getTitle()->getPrefixedText() ] + ) ) { # Flag to hide all contents of the archived revisions @@ -2126,7 +2130,13 @@ class Article implements Page { $form->appendContent( $fieldset, new OOUI\HtmlSnippet( - Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) ) + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext() + ->getCsrfTokenSet() + ->getToken( [ 'delete', $title->getPrefixedText() ] ) + ->toString() + ) ) ); diff --git a/includes/page/ImageHistoryList.php b/includes/page/ImageHistoryList.php index c686199f47e6..340f4f48f3f0 100644 --- a/includes/page/ImageHistoryList.php +++ b/includes/page/ImageHistoryList.php @@ -214,7 +214,7 @@ class ImageHistoryList extends ContextSource { [ 'target' => $this->title->getPrefixedText(), 'file' => $img, - 'token' => $user->getEditToken( $img ) + 'token' => $this->getCsrfTokenSet()->getToken( $img )->toString(), ] ); } else { diff --git a/includes/resourceloader/ResourceLoaderUserOptionsModule.php b/includes/resourceloader/ResourceLoaderUserOptionsModule.php index 06496702324d..2cacb560c9d3 100644 --- a/includes/resourceloader/ResourceLoaderUserOptionsModule.php +++ b/includes/resourceloader/ResourceLoaderUserOptionsModule.php @@ -50,15 +50,15 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule { * @return string JavaScript code */ public function getScript( ResourceLoaderContext $context ) { - $user = $context->getUserObj(); - + $tokenSet = RequestContext::getMain()->getCsrfTokenSet(); $tokens = [ - 'patrolToken' => $user->getEditToken( 'patrol' ), - 'watchToken' => $user->getEditToken( 'watch' ), - 'csrfToken' => $user->getEditToken(), + 'patrolToken' => $tokenSet->getToken( 'patrol' )->toString(), + 'watchToken' => $tokenSet->getToken( 'watch' )->toString(), + 'csrfToken' => $tokenSet->getToken()->toString(), ]; $script = 'mw.user.tokens.set(' . $context->encodeJson( $tokens ) . ');'; + $user = $context->getUserObj(); $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup(); $options = $userOptionsLookup->getOptions( $user, UserOptionsLookup::EXCLUDE_DEFAULTS ); // Optimisation: Only output this function call if the user has non-default settings. diff --git a/includes/revisiondelete/RevDelArchivedFileItem.php b/includes/revisiondelete/RevDelArchivedFileItem.php index 3a710e839ffc..34879caadd8b 100644 --- a/includes/revisiondelete/RevDelArchivedFileItem.php +++ b/includes/revisiondelete/RevDelArchivedFileItem.php @@ -94,7 +94,7 @@ class RevDelArchivedFileItem extends RevDelFileItem { [ 'target' => $this->list->getPageName(), 'file' => $key, - 'token' => $this->list->getUser()->getEditToken( $key ) + 'token' => $this->list->getCsrfTokenSet()->getToken( $key )->toString(), ] ); } @@ -124,7 +124,7 @@ class RevDelArchivedFileItem extends RevDelFileItem { [ 'target' => $this->list->getPageName(), 'file' => $file->getKey(), - 'token' => $user->getEditToken( $file->getKey() ) + 'token' => $this->list->getCsrfTokenSet()->getToken( $file->getKey() )->toString(), ] ), ]; diff --git a/includes/revisiondelete/RevDelFileItem.php b/includes/revisiondelete/RevDelFileItem.php index 4b51cfd93718..ac8d07408b45 100644 --- a/includes/revisiondelete/RevDelFileItem.php +++ b/includes/revisiondelete/RevDelFileItem.php @@ -154,8 +154,9 @@ class RevDelFileItem extends RevDelItem { [ 'target' => $this->list->getPageName(), 'file' => $this->file->getArchiveName(), - 'token' => $this->list->getUser()->getEditToken( - $this->file->getArchiveName() ) + 'token' => $this->list->getCsrfTokenSet() + ->getToken( $this->file->getArchiveName() ) + ->toString(), ] ); } @@ -235,7 +236,9 @@ class RevDelFileItem extends RevDelItem { [ 'target' => $this->list->getPageName(), 'file' => $file->getArchiveName(), - 'token' => $user->getEditToken( $file->getArchiveName() ) + 'token' => $this->list->getCsrfTokenSet() + ->getToken( $file->getArchiveName() ) + ->toString(), ] ), ]; diff --git a/includes/search/searchwidgets/SearchFormWidget.php b/includes/search/searchwidgets/SearchFormWidget.php index 56f791fbc70b..9c8ee5417e12 100644 --- a/includes/search/searchwidgets/SearchFormWidget.php +++ b/includes/search/searchwidgets/SearchFormWidget.php @@ -317,10 +317,13 @@ class SearchFormWidget { false, // The token goes here rather than in a hidden field so it // is only sent when necessary (not every form submission) - [ 'value' => $user->getEditToken( - 'searchnamespace', - $this->specialSearch->getRequest() - ) ] + [ + 'value' => $this->specialSearch + ->getContext() + ->getCsrfTokenSet() + ->getToken( 'searchnamespace' ) + ->toString(), + ] ); } diff --git a/includes/specials/SpecialEditTags.php b/includes/specials/SpecialEditTags.php index 65b94d4534d2..732c67e366cd 100644 --- a/includes/specials/SpecialEditTags.php +++ b/includes/specials/SpecialEditTags.php @@ -20,6 +20,7 @@ */ use MediaWiki\Permissions\PermissionManager; +use MediaWiki\Session\CsrfTokenSet; /** * Special page for adding and removing change tags to individual revisions. @@ -288,7 +289,13 @@ class SpecialEditTags extends UnlistedSpecialPage { '</td>' . "</tr>\n" . Xml::closeElement( 'table' ) . - Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) . + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext() + ->getCsrfTokenSet() + ->getToken() + ->toString() + ) . Html::hidden( 'target', $this->targetObj->getPrefixedText() ) . Html::hidden( 'type', $this->typeName ) . Html::hidden( 'ids', implode( ',', $this->ids ) ) . @@ -400,8 +407,7 @@ class SpecialEditTags extends UnlistedSpecialPage { protected function submit() { // Check edit token on submission $request = $this->getRequest(); - $token = $request->getVal( 'wpEditToken' ); - if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) { + if ( $this->submitClicked && !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { $this->getOutput()->addWikiMsg( 'sessionfailure' ); return false; } diff --git a/includes/specials/SpecialEmailUser.php b/includes/specials/SpecialEmailUser.php index 0a7d4cac0790..e613e54b61e1 100644 --- a/includes/specials/SpecialEmailUser.php +++ b/includes/specials/SpecialEmailUser.php @@ -23,6 +23,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Preferences\MultiUsernameFilter; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserNamePrefixSearch; use MediaWiki\User\UserNameUtils; use MediaWiki\User\UserOptionsLookup; @@ -146,7 +147,7 @@ class SpecialEmailUser extends UnlistedSpecialPage { // error out if sending user cannot do this $error = self::getPermissionsError( $this->getUser(), - $this->getRequest()->getVal( 'wpEditToken' ), + $this->getRequest()->getVal( CsrfTokenSet::DEFAULT_FIELD_NAME ), $this->getConfig() ); diff --git a/includes/specials/SpecialExpandTemplates.php b/includes/specials/SpecialExpandTemplates.php index 2c13058ac51b..5b6bbd4443ba 100644 --- a/includes/specials/SpecialExpandTemplates.php +++ b/includes/specials/SpecialExpandTemplates.php @@ -293,7 +293,7 @@ class SpecialExpandTemplates extends SpecialPage { // do not show the preview unless anonymous editing is allowed. if ( $user->isAnon() && !$this->getAuthority()->isAllowed( 'edit' ) ) { $error = [ 'expand_templates_preview_fail_html_anon' ]; - } elseif ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ), '', $request ) ) { + } elseif ( !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { $error = [ 'expand_templates_preview_fail_html' ]; } else { $error = false; diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index 7d7fafdc216f..0e7a2e285aa0 100644 --- a/includes/specials/SpecialImport.php +++ b/includes/specials/SpecialImport.php @@ -139,7 +139,7 @@ class SpecialImport extends SpecialPage { $user = $this->getUser(); $fullInterwikiPrefix = null; - if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) { + if ( !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { $source = Status::newFatal( 'import-token-mismatch' ); } elseif ( $sourceName === 'upload' ) { $isUpload = true; diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php index a683e6f144ef..67aed2520e68 100644 --- a/includes/specials/SpecialMergeHistory.php +++ b/includes/specials/SpecialMergeHistory.php @@ -25,6 +25,7 @@ use MediaWiki\Cache\LinkBatchFactory; use MediaWiki\Page\MergeHistoryFactory; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionStore; +use MediaWiki\Session\CsrfTokenSet; use Wikimedia\Rdbms\ILoadBalancer; /** @@ -124,7 +125,7 @@ class SpecialMergeHistory extends SpecialPage { $this->mComment = $request->getText( 'wpComment' ); $this->mMerge = $request->wasPosted() - && $this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) ); + && $this->getContext()->getCsrfTokenSet()->matchTokenField(); // target page if ( $this->mSubmitted ) { @@ -306,7 +307,10 @@ class SpecialMergeHistory extends SpecialPage { $misc .= Html::hidden( 'destID', $this->mDestObj->getArticleID() ); $misc .= Html::hidden( 'target', $this->mTarget ); $misc .= Html::hidden( 'dest', $this->mDest ); - $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); + $misc .= Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext()->getCsrfTokenSet()->getToken()->toString() + ); $misc .= Xml::closeElement( 'form' ); $out->addHTML( $misc ); diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php index 526790021568..395c82ffb9ba 100644 --- a/includes/specials/SpecialMovepage.php +++ b/includes/specials/SpecialMovepage.php @@ -27,6 +27,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Page\MovePageFactory; use MediaWiki\Page\WikiPageFactory; use MediaWiki\Permissions\PermissionManager; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserOptionsLookup; use MediaWiki\Watchlist\WatchlistManager; use Wikimedia\Rdbms\ILoadBalancer; @@ -206,7 +207,7 @@ class MovePageForm extends UnlistedSpecialPage { $this->watch = $request->getCheck( 'wpWatch' ) && $user->isRegistered(); if ( $request->getVal( 'action' ) == 'submit' && $request->wasPosted() - && $user->matchEditToken( $request->getVal( 'wpEditToken' ) ) + && $this->getContext()->getCsrfTokenSet()->matchTokenField() ) { $this->doSubmit(); } else { @@ -596,7 +597,10 @@ class MovePageForm extends UnlistedSpecialPage { new OOUI\HtmlSnippet( $hiddenFields . Html::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) . - Html::hidden( 'wpEditToken', $user->getEditToken() ) + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext()->getCsrfTokenSet()->getToken()->toString() + ) ) ); diff --git a/includes/specials/SpecialRevisionDelete.php b/includes/specials/SpecialRevisionDelete.php index efb0f04dc014..1da6b275efe6 100644 --- a/includes/specials/SpecialRevisionDelete.php +++ b/includes/specials/SpecialRevisionDelete.php @@ -23,6 +23,7 @@ use MediaWiki\Permissions\PermissionManager; use MediaWiki\Revision\RevisionRecord; +use MediaWiki\Session\CsrfTokenSet; /** * Special page allowing users with the appropriate permissions to view @@ -43,9 +44,6 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { /** @var string Archive name, for reviewing deleted files */ private $archiveName; - /** @var string Edit token for securing image views against XSS */ - private $token; - /** @var Title Title object for target parameter */ private $targetObj; @@ -161,7 +159,6 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { # For reviewing deleted files... $this->archiveName = $request->getVal( 'file' ); - $this->token = $request->getVal( 'token' ); if ( $this->archiveName && $this->targetObj ) { $this->tryShowFile( $this->archiveName ); @@ -343,7 +340,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { throw new PermissionsError( 'deletedtext' ); } } - if ( !$user->matchEditToken( $this->token, $archiveName ) ) { + if ( !$this->getContext()->getCsrfTokenSet()->matchTokenField( 'token', $archiveName ) ) { $lang = $this->getLanguage(); $this->getOutput()->addWikiMsg( 'revdelete-show-file-confirm', $this->targetObj->getText(), @@ -355,7 +352,10 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { 'action' => $this->getPageTitle()->getLocalURL( [ 'target' => $this->targetObj->getPrefixedDBkey(), 'file' => $archiveName, - 'token' => $user->getEditToken( $archiveName ), + 'token' => $this->getContext() + ->getCsrfTokenSet() + ->getToken( $archiveName ) + ->toString(), ] ) ] ) . @@ -494,7 +494,10 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { '</td>' . "</tr>\n" . Xml::closeElement( 'table' ) . - Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) . + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext()->getCsrfTokenSet()->getToken()->toString() + ) . Html::hidden( 'target', $this->targetObj->getPrefixedText() ) . Html::hidden( 'type', $this->typeName ) . Html::hidden( 'ids', implode( ',', $this->ids ) ) . @@ -624,8 +627,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { */ protected function submit() { # Check edit token on submission - $token = $this->getRequest()->getVal( 'wpEditToken' ); - if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) { + if ( $this->submitClicked && !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { $this->getOutput()->addWikiMsg( 'sessionfailure' ); return false; diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 5d64cc6ad8e0..5916cdf01faa 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -728,11 +728,8 @@ class SpecialSearch extends SpecialPage { $request = $this->getRequest(); if ( $user->isRegistered() && - $user->matchEditToken( - $request->getVal( 'nsRemember' ), - 'searchnamespace', - $request - ) && !$this->readOnlyMode->isReadOnly() + $this->getContext()->getCsrfTokenSet()->matchTokenField( 'nsRemember', 'searchnamespace' ) && + !$this->readOnlyMode->isReadOnly() ) { // Reset namespace preferences: namespaces are not searched // when they're not mentioned in the URL parameters. diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php index fdc2a41e2898..5276049b13a8 100644 --- a/includes/specials/SpecialTags.php +++ b/includes/specials/SpecialTags.php @@ -21,6 +21,8 @@ * @ingroup SpecialPage */ +use MediaWiki\Session\CsrfTokenSet; + /** * A special page that lists tags for edits * @@ -314,7 +316,7 @@ class SpecialTags extends SpecialPage { // fool HTMLForm into thinking the form hasn't been submitted yet. Otherwise // we get into an infinite loop! - $context->getRequest()->unsetVal( 'wpEditToken' ); + $context->getRequest()->unsetVal( CsrfTokenSet::DEFAULT_FIELD_NAME ); $headerText = $this->msg( 'tags-create-warnings-above', $tag, count( $status->getWarningsArray() ) )->parseAsBlock() . diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index 7f38bb8d6e93..bc116da09548 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -29,6 +29,7 @@ use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionRenderer; use MediaWiki\Revision\RevisionStore; use MediaWiki\Revision\SlotRecord; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\NameTableAccessException; use MediaWiki\Storage\NameTableStore; use MediaWiki\User\UserOptionsLookup; @@ -171,8 +172,7 @@ class SpecialUndelete extends SpecialPage { $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : ''; $this->mFilename = $request->getVal( 'file' ); - $posted = $request->wasPosted() && - $user->matchEditToken( $request->getVal( 'wpEditToken' ) ); + $posted = $request->wasPosted() && $this->getContext()->getCsrfTokenSet()->matchTokenField(); $this->mRestore = $request->getCheck( 'restore' ) && $posted; $this->mRevdel = $request->getCheck( 'revdel' ) && $posted; $this->mInvert = $request->getCheck( 'invert' ) && $posted; @@ -314,7 +314,7 @@ class SpecialUndelete extends SpecialPage { } else { throw new PermissionsError( 'deletedtext' ); } - } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) { + } elseif ( !$this->getContext()->getCsrfTokenSet()->matchToken( $this->mToken, $this->mFilename ) ) { $this->showFileConfirmationForm( $this->mFilename ); } else { $this->showFile( $this->mFilename ); @@ -666,8 +666,8 @@ class SpecialUndelete extends SpecialPage { 'value' => $timestamp ] ) . Xml::element( 'input', [ 'type' => 'hidden', - 'name' => 'wpEditToken', - 'value' => $user->getEditToken() ] ) . + 'name' => CsrfTokenSet::DEFAULT_FIELD_NAME, + 'value' => $this->getContext()->getCsrfTokenSet()->getToken()->toString() ] ) . new OOUI\FieldLayout( new OOUI\Widget( [ 'content' => new OOUI\HorizontalLayout( [ @@ -809,7 +809,7 @@ class SpecialUndelete extends SpecialPage { 'action' => $this->getPageTitle()->getLocalURL( [ 'target' => $this->mTarget, 'file' => $key, - 'token' => $user->getEditToken( $key ), + 'token' => $this->getContext()->getCsrfTokenSet()->getToken()->toString(), ] ), ] ) . @@ -988,7 +988,9 @@ class SpecialUndelete extends SpecialPage { ] ), new OOUI\HtmlSnippet( Html::hidden( 'target', $this->mTarget ) . - Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext()->getCsrfTokenSet()->getToken()->toString() ) ) ); } @@ -1039,7 +1041,10 @@ class SpecialUndelete extends SpecialPage { if ( $this->mAllowed ) { # Slip in the hidden controls here $misc = Html::hidden( 'target', $this->mTarget ); - $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); + $misc .= Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext()->getCsrfTokenSet()->getToken()->toString() + ); $history .= $misc; $form->appendContent( new OOUI\HtmlSnippet( $history ) ); @@ -1251,7 +1256,7 @@ class SpecialUndelete extends SpecialPage { [ 'target' => $this->mTargetObj->getPrefixedText(), 'file' => $key, - 'token' => $user->getEditToken( $key ) + 'token' => $this->getContext()->getCsrfTokenSet()->getToken( $key )->toString(), ] ); diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php index e1abe163a64b..909052dd3259 100644 --- a/includes/specials/SpecialUpload.php +++ b/includes/specials/SpecialUpload.php @@ -156,8 +156,7 @@ class SpecialUpload extends SpecialPage { || $request->getCheck( 'wpReUpload' ); // b/w compat // If it was posted check for the token (no remote POST'ing with user credentials) - $token = $request->getVal( 'wpEditToken' ); - $this->mTokenOk = $this->getUser()->matchEditToken( $token ); + $this->mTokenOk = $this->getContext()->getCsrfTokenSet()->matchTokenField(); $this->uploadFormTextTop = ''; $this->uploadFormTextAfterSummary = ''; diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php index 96d20fa92983..92660b42fc60 100644 --- a/includes/specials/SpecialUserrights.php +++ b/includes/specials/SpecialUserrights.php @@ -22,6 +22,7 @@ */ use MediaWiki\MediaWikiServices; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserGroupManager; use MediaWiki\User\UserGroupManagerFactory; use MediaWiki\User\UserNamePrefixSearch; @@ -186,7 +187,10 @@ class UserrightsPage extends SpecialPage { $request->wasPosted() && $request->getCheck( 'saveusergroups' ) && $this->mTarget !== null && - $user->matchEditToken( $request->getVal( 'wpEditToken' ), $this->mTarget ) + $this->getContext()->getCsrfTokenSet()->matchTokenField( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->mTarget + ) ) { /* * If the user is blocked and they only have "partial" access @@ -770,7 +774,10 @@ class UserrightsPage extends SpecialPage { ] ) . Html::hidden( 'user', $this->mTarget ) . - Html::hidden( 'wpEditToken', $this->getUser()->getEditToken( $this->mTarget ) ) . + Html::hidden( + CsrfTokenSet::DEFAULT_FIELD_NAME, + $this->getContext()->getCsrfTokenSet()->getToken( $this->mTarget )->toString() + ) . Html::hidden( 'conflictcheck-originalgroups', implode( ',', $user->getGroups() ) diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index 16c54d0858d2..af53ee03c8f5 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -117,7 +117,7 @@ class SpecialWatchlist extends ChangesListSpecialPage { if ( ( $config->get( 'EnotifWatchlist' ) || $config->get( 'ShowUpdatedMarker' ) ) && $request->getVal( 'reset' ) && $request->wasPosted() - && $user->matchEditToken( $request->getVal( 'token' ) ) + && $this->getContext()->getCsrfTokenSet()->matchTokenField( 'token' ) ) { $this->watchlistManager->clearAllUserNotifications( $user ); $output->redirect( $this->getPageTitle()->getFullURL( $opts->getChangedValues() ) ); @@ -863,7 +863,7 @@ class SpecialWatchlist extends ChangesListSpecialPage { 'id' => 'mw-watchlist-resetbutton' ] ) . "\n" . Xml::submitButton( $this->msg( 'enotif_reset' )->text(), [ 'name' => 'mw-watchlist-reset-submit' ] ) . "\n" . - Html::hidden( 'token', $user->getEditToken() ) . "\n" . + Html::hidden( 'token', $this->getContext()->getCsrfTokenSet()->getToken()->toString() ) . "\n" . Html::hidden( 'reset', 'all' ) . "\n"; foreach ( $nondefaults as $key => $value ) { $form .= Html::hidden( $key, $value ) . "\n"; diff --git a/tests/phpunit/includes/EditPageConstraintsTest.php b/tests/phpunit/includes/EditPageConstraintsTest.php index 1bc904983786..95f6e4ee3886 100644 --- a/tests/phpunit/includes/EditPageConstraintsTest.php +++ b/tests/phpunit/includes/EditPageConstraintsTest.php @@ -2,6 +2,7 @@ use MediaWiki\EditPage\SpamChecker; use MediaWiki\Permissions\PermissionManager; +use MediaWiki\Session\CsrfTokenSet; /** * Integration tests for the various edit constraints, ensuring @@ -102,14 +103,6 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase { ); } - if ( !isset( $edit['wpEditToken'] ) ) { - $edit['wpEditToken'] = $user->getEditToken(); - } - - if ( !isset( $edit['wpEdittime'] ) && !isset( $edit['editRevId'] ) ) { - $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : ''; - } - if ( !isset( $edit['wpStarttime'] ) ) { $edit['wpStarttime'] = wfTimestampNow(); } @@ -119,7 +112,12 @@ class EditPageConstraintsTest extends MediaWikiLangTestCase { } $req = new FauxRequest( $edit, true ); // session ?? - + if ( !isset( $edit['wpEditToken'] ) ) { + $req->setVal( + 'wpEditToken', + ( new CsrfTokenSet( $req ) )->getToken()->toString() + ); + } $context = new RequestContext(); $context->setRequest( $req ); $context->setTitle( $title ); diff --git a/tests/phpunit/includes/EditPageTest.php b/tests/phpunit/includes/EditPageTest.php index 2725b8c61eb5..d8aebe2e573b 100644 --- a/tests/phpunit/includes/EditPageTest.php +++ b/tests/phpunit/includes/EditPageTest.php @@ -2,6 +2,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Revision\RevisionRecord; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\EditResult; use MediaWiki\User\UserIdentity; use Wikimedia\TestingAccessWrapper; @@ -157,10 +158,6 @@ class EditPageTest extends MediaWikiLangTestCase { $this->assertEditedTextEquals( $baseText, $currentText ); } - if ( !isset( $edit['wpEditToken'] ) ) { - $edit['wpEditToken'] = $user->getEditToken(); - } - if ( !isset( $edit['wpEdittime'] ) && !isset( $edit['editRevId'] ) ) { $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : ''; } @@ -174,6 +171,12 @@ class EditPageTest extends MediaWikiLangTestCase { } $req = new FauxRequest( $edit, true ); // session ?? + if ( !isset( $edit['wpEditToken'] ) ) { + $req->setVal( + 'wpEditToken', + ( new CsrfTokenSet( $req ) )->getToken()->toString() + ); + } $context = new RequestContext(); $context->setRequest( $req ); @@ -781,11 +784,8 @@ hello * @covers EditPage */ public function testCheckDirectEditingDisallowed_forNonTextContent() { - $user = $this->getTestUser()->getUser(); - $edit = [ 'wpTextbox1' => serialize( 'non-text content' ), - 'wpEditToken' => $user->getEditToken(), 'wpEdittime' => '', 'editRevId' => 0, 'wpStarttime' => wfTimestampNow(), @@ -810,11 +810,8 @@ hello } } ); - $user = $this->getTestUser()->getUser(); - $status = $this->doEditDummyNonTextPage( [ 'wpTextbox1' => 'some text', - 'wpEditToken' => $user->getEditToken(), 'wpEdittime' => '', 'editRevId' => 0, 'wpStarttime' => wfTimestampNow(), @@ -837,11 +834,8 @@ hello } } ); - $user = $this->getTestUser()->getUser(); - $status = $this->doEditDummyNonTextPage( [ 'wpTextbox1' => 'some text', - 'wpEditToken' => $user->getEditToken(), 'wpEdittime' => '', 'editRevId' => 0, 'wpStarttime' => wfTimestampNow(), @@ -863,6 +857,10 @@ hello $ep->setContextTitle( $title ); $req = new FauxRequest( $edit, true ); + $req->setVal( + 'wpEditToken', + ( new CsrfTokenSet( $req ) )->getToken()->toString() + ); $ep->importFormData( $req ); return $ep->internalAttemptSave( $result, false ); diff --git a/tests/phpunit/includes/OutputPageTest.php b/tests/phpunit/includes/OutputPageTest.php index 5929684df72d..e4f98150c18b 100644 --- a/tests/phpunit/includes/OutputPageTest.php +++ b/tests/phpunit/includes/OutputPageTest.php @@ -8,7 +8,6 @@ use MediaWiki\Page\PageReferenceValue; use MediaWiki\Page\PageStoreRecord; use MediaWiki\Permissions\Authority; use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait; -use PHPUnit\Framework\MockObject\MockObject; use Wikimedia\DependencyStore\KeyValueDependencyStore; use Wikimedia\TestingAccessWrapper; @@ -3206,64 +3205,40 @@ class OutputPageTest extends MediaWikiIntegrationTestCase { $this->assertArraySubmapSame( $expectedEditableConfig, $op->getJSVars() ); } - /** - * @param bool $registered - * @param bool $matchToken - * @return MockObject|User - */ - private function mockUser( bool $registered, bool $matchToken ) { - $user = $this->createNoOpMock( User::class, [ 'isRegistered', 'matchEditToken' ] ); - $user->method( 'isRegistered' )->willReturn( $registered ); - $user->method( 'matchEditToken' )->willReturn( $matchToken ); - return $user; - } - public function provideUserCanPreview() { yield 'all good' => [ - 'performer' => $this->mockUserAuthorityWithPermissions( - $this->mockUser( true, true ), - [ 'edit' ] - ), + 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), + 'matchToken' => true, 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), true ]; yield 'get request' => [ - 'performer' => $this->mockUserAuthorityWithPermissions( - $this->mockUser( true, true ), - [ 'edit' ] - ), + 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), + 'matchToken' => true, 'request' => new FauxRequest( [ 'action' => 'submit' ], false ), false ]; yield 'not a submit action' => [ - 'performer' => $this->mockUserAuthorityWithPermissions( - $this->mockUser( true, true ), - [ 'edit' ] - ), + 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), + 'matchToken' => true, 'request' => new FauxRequest( [ 'action' => 'something' ], true ), false ]; yield 'anon can not' => [ - 'performer' => $this->mockUserAuthorityWithPermissions( - $this->mockUser( false, true ), - [ 'edit' ] - ), + 'performer' => $this->mockAnonAuthorityWithPermissions( [ 'edit' ] ), + 'matchToken' => true, 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), false ]; yield 'token not match' => [ - 'performer' => $this->mockUserAuthorityWithPermissions( - $this->mockUser( true, false ), - [ 'edit' ] - ), + 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), + 'matchToken' => false, 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), false ]; yield 'no permission' => [ - 'performer' => $this->mockUserAuthorityWithoutPermissions( - $this->mockUser( true, true ), - [ 'edit' ] - ), + 'performer' => $this->mockRegisteredAuthorityWithoutPermissions( [ 'edit' ] ), + 'matchToken' => true, 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), false ]; @@ -3273,7 +3248,9 @@ class OutputPageTest extends MediaWikiIntegrationTestCase { * @dataProvider provideUserCanPreview * @covers OutputPage::userCanPreview */ - public function testUserCanPreview( Authority $performer, WebRequest $request, bool $expected ) { + public function testUserCanPreview( Authority $performer, bool $matchToken, WebRequest $request, bool $expected ) { + // We don't set the user in the session, so we logged out token should be good enough. + $request->setVal( 'wpEditToken', $matchToken ? new LoggedOutEditToken() : 'invalid' ); $op = $this->newInstance( [], $request, null, $performer ); $this->assertSame( $expected, $op->userCanPreview() ); } diff --git a/tests/phpunit/includes/actions/RollbackActionTest.php b/tests/phpunit/includes/actions/RollbackActionTest.php index 4c53d4bfe6c5..eed131b77647 100644 --- a/tests/phpunit/includes/actions/RollbackActionTest.php +++ b/tests/phpunit/includes/actions/RollbackActionTest.php @@ -6,6 +6,7 @@ use Article; use DerivativeContext; use ErrorPageError; use FauxRequest; +use MediaWiki\Session\CsrfTokenSet; use MediaWikiIntegrationTestCase; use RequestContext; use RollbackAction; @@ -43,6 +44,13 @@ class RollbackActionTest extends MediaWikiIntegrationTestCase { private function getRollbackAction( WebRequest $request ) { $context = new DerivativeContext( RequestContext::getMain() ); $context->setTitle( $this->testPage ); + $request->getSession()->setUser( $this->sysop ); + if ( !$request->getVal( 'token' ) ) { + $request->setVal( + 'token', + ( new CsrfTokenSet( $request ) )->getToken( 'rollback' )->toString() + ); + } $context->setRequest( $request ); $context->setUser( $this->sysop ); return new RollbackAction( Article::newFromTitle( $this->testPage, $context ), $context ); @@ -94,7 +102,6 @@ class RollbackActionTest extends MediaWikiIntegrationTestCase { public function testRollback() { $request = new FauxRequest( [ 'from' => $this->vandal->getName(), - 'token' => $this->sysop->getEditToken( 'rollback' ), ] ); $rollbackAction = $this->getRollbackAction( $request ); $rollbackAction->handleRollbackRequest(); @@ -118,7 +125,6 @@ class RollbackActionTest extends MediaWikiIntegrationTestCase { public function testRollbackMarkBot() { $request = new FauxRequest( [ 'from' => $this->vandal->getName(), - 'token' => $this->sysop->getEditToken( 'rollback' ), 'bot' => true, ] ); $rollbackAction = $this->getRollbackAction( $request ); diff --git a/tests/phpunit/includes/actions/WatchActionTest.php b/tests/phpunit/includes/actions/WatchActionTest.php index 6180a0106394..4526eee64c31 100644 --- a/tests/phpunit/includes/actions/WatchActionTest.php +++ b/tests/phpunit/includes/actions/WatchActionTest.php @@ -443,8 +443,8 @@ class WatchActionTest extends MediaWikiIntegrationTestCase { $this->hideDeprecated( 'WatchAction::getWatchToken' ); $user = $this->createMock( User::class ); $user->expects( $this->once() ) - ->method( 'getEditToken' ) - ->with( 'watch' ); + ->method( 'getRequest' ) + ->willReturn( new FauxRequest() ); WatchAction::getWatchToken( $this->watchAction->getTitle(), $user, 'INVALID_ACTION' ); } @@ -456,7 +456,9 @@ class WatchActionTest extends MediaWikiIntegrationTestCase { public function testGetWatchTokenProxiesUserGetEditToken() { $this->hideDeprecated( 'WatchAction::getWatchToken' ); $user = $this->createMock( User::class ); - $user->expects( $this->once() )->method( 'getEditToken' ); + $user->expects( $this->once() ) + ->method( 'getRequest' ) + ->willReturn( new FauxRequest() ); WatchAction::getWatchToken( $this->watchAction->getTitle(), $user ); } diff --git a/tests/phpunit/includes/api/ApiDeleteTest.php b/tests/phpunit/includes/api/ApiDeleteTest.php index 742ee349a4c9..647d2e34c5d1 100644 --- a/tests/phpunit/includes/api/ApiDeleteTest.php +++ b/tests/phpunit/includes/api/ApiDeleteTest.php @@ -98,11 +98,10 @@ class ApiDeleteTest extends ApiTestCase { // test deletion without permission try { $user = new User(); - $apiResult = $this->doApiRequest( [ + $apiResult = $this->doApiRequestWithToken( [ 'action' => 'delete', 'title' => $name, - 'token' => $user->getEditToken(), - ], null, null, $user ); + ], null, $user ); } finally { $this->assertTrue( Title::newFromText( $name )->exists() ); } diff --git a/tests/phpunit/includes/api/ApiLogoutTest.php b/tests/phpunit/includes/api/ApiLogoutTest.php index 066cbc478a06..446c07469c0b 100644 --- a/tests/phpunit/includes/api/ApiLogoutTest.php +++ b/tests/phpunit/includes/api/ApiLogoutTest.php @@ -46,8 +46,7 @@ class ApiLogoutTest extends ApiTestCase { $user = $this->getTestSysop()->getUser(); $this->assertTrue( $user->isRegistered(), 'sanity check' ); - // Logic copied from SkinTemplate. - $token = $user->getEditToken( 'logoutToken', $wgRequest ); + $token = $wgRequest->getSession()->getToken( 'logoutToken' )->toString(); $this->doUserLogout( $token, $user ); $this->assertFalse( $user->isRegistered() ); diff --git a/tests/phpunit/includes/api/ApiUserrightsTest.php b/tests/phpunit/includes/api/ApiUserrightsTest.php index 2d617e176446..9d115868546e 100644 --- a/tests/phpunit/includes/api/ApiUserrightsTest.php +++ b/tests/phpunit/includes/api/ApiUserrightsTest.php @@ -2,6 +2,7 @@ use MediaWiki\Block\DatabaseBlock; use MediaWiki\MediaWikiServices; +use MediaWiki\Session\CsrfTokenSet; /** * @group API @@ -244,14 +245,11 @@ class ApiUserrightsTest extends ApiTestCase { $sysop = $this->getTestSysop()->getUser(); $user = $this->getMutableTestUser()->getUser(); - $token = $sysop->getEditToken( $user->getName() ); - - $res = $this->doApiRequest( [ + $res = $this->doApiRequestWithToken( [ 'action' => 'userrights', 'user' => $user->getName(), 'add' => 'sysop', - 'token' => $token, - ] ); + ], null, $sysop ); $user->clearInstanceCache(); $this->assertSame( [ 'sysop' ], $user->getGroups() ); @@ -269,17 +267,17 @@ class ApiUserrightsTest extends ApiTestCase { * @return ApiUserrights */ private function getMockForProcessingExpiries( $canProcessExpiries ) { - $sysop = $this->getTestSysop()->getUser(); $user = $this->getMutableTestUser()->getUser(); - - $token = $sysop->getEditToken( 'userrights' ); - - $main = new ApiMain( new FauxRequest( [ + $request = new FauxRequest( [ 'action' => 'userrights', 'user' => $user->getName(), 'add' => 'sysop', - 'token' => $token, - ] ) ); + ] ); + $request->setVal( + 'token', + ( new CsrfTokenSet( $request ) )->getToken( 'userrights' )->toString() + ); + $main = new ApiMain( $request ); $mockUserRightsPage = $this->getMockBuilder( UserrightsPage::class ) ->onlyMethods( [ 'canProcessExpiries' ] ) diff --git a/tests/phpunit/includes/api/ApiWatchTest.php b/tests/phpunit/includes/api/ApiWatchTest.php index 7f31b61e10e7..381b130a2a3d 100644 --- a/tests/phpunit/includes/api/ApiWatchTest.php +++ b/tests/phpunit/includes/api/ApiWatchTest.php @@ -235,13 +235,12 @@ class ApiWatchTest extends ApiTestCase { // This (and assertTrue below) are mostly for completeness. $this->assertFalse( $watchlistManager->isWatched( $contextUser, $title ) ); - $data = $this->doApiRequest( [ + $data = $this->doApiRequestWithToken( [ 'action' => 'rollback', 'title' => 'Help:UTPage', 'user' => $revUser, - 'token' => $contextUser->getEditToken( 'rollback' ), 'watchlist' => 'watch' - ] ); + ], null, $contextUser ); $this->assertArrayHasKey( 'rollback', $data[0] ); $this->assertArrayHasKey( 'title', $data[0]['rollback'] ); diff --git a/tests/phpunit/includes/specials/SpecialPageExecutor.php b/tests/phpunit/includes/specials/SpecialPageExecutor.php index 887d11d43dc9..650fdf494a5e 100644 --- a/tests/phpunit/includes/specials/SpecialPageExecutor.php +++ b/tests/phpunit/includes/specials/SpecialPageExecutor.php @@ -92,11 +92,13 @@ class SpecialPageExecutor { */ private function setEditTokenFromUser( DerivativeContext $context ) { $request = $context->getRequest(); - // Edits via GET are a security issue and should not succeed. On the other hand, not all // POST requests are edits, but should ignore unused parameters. if ( !$request->getCheck( 'wpEditToken' ) && $request->wasPosted() ) { - $request->setVal( 'wpEditToken', $context->getUser()->getEditToken() ); + $request->setVal( + 'wpEditToken', + $context->getCsrfTokenSet()->getToken()->toString() + ); } } diff --git a/tests/phpunit/includes/upload/UploadFromUrlTest.php b/tests/phpunit/includes/upload/UploadFromUrlTest.php index 62d7a6cc85ee..0e9c26b0a44b 100644 --- a/tests/phpunit/includes/upload/UploadFromUrlTest.php +++ b/tests/phpunit/includes/upload/UploadFromUrlTest.php @@ -105,7 +105,6 @@ class UploadFromUrlTest extends ApiTestCase { * @depends testClearQueue */ public function testSetupUrlDownload( $data ) { - $token = $this->user->getEditToken(); $exception = false; try { @@ -120,10 +119,9 @@ class UploadFromUrlTest extends ApiTestCase { $exception = false; try { - $this->doApiRequest( [ + $this->doApiRequestWithToken( [ 'action' => 'upload', - 'token' => $token, - ], $data ); + ], $data, $this->user ); } catch ( ApiUsageException $e ) { $exception = true; $this->assertEquals( 'One of the parameters "filekey", "file" and "url" is required.', @@ -133,11 +131,10 @@ class UploadFromUrlTest extends ApiTestCase { $exception = false; try { - $this->doApiRequest( [ + $this->doApiRequestWithToken( [ 'action' => 'upload', 'url' => 'http://www.example.com/test.png', - 'token' => $token, - ], $data ); + ], $data, $this->user ); } catch ( ApiUsageException $e ) { $exception = true; $this->assertEquals( 'The "filename" parameter must be set.', $e->getMessage() ); @@ -147,12 +144,11 @@ class UploadFromUrlTest extends ApiTestCase { $this->user->removeGroup( 'sysop' ); $exception = false; try { - $this->doApiRequest( [ + $this->doApiRequestWithToken( [ 'action' => 'upload', 'url' => 'http://www.example.com/test.png', 'filename' => 'UploadFromUrlTest.png', - 'token' => $token, - ], $data ); + ], $data, $this->user ); } catch ( ApiUsageException $e ) { $exception = true; // Two error messages are possible depending on the number of groups in the wiki with upload rights: @@ -183,16 +179,13 @@ class UploadFromUrlTest extends ApiTestCase { $file = __DIR__ . '/../../data/upload/png-plain.png'; $this->installMockHttp( file_get_contents( $file ) ); - $token = $this->user->getEditToken(); - $this->user->addGroup( 'users' ); - $data = $this->doApiRequest( [ + $data = $this->doApiRequestWithToken( [ 'action' => 'upload', 'filename' => 'UploadFromUrlTest.png', 'url' => 'http://upload.wikimedia.org/wikipedia/mediawiki/b/bc/Wiki.png', 'ignorewarnings' => true, - 'token' => $token, - ], $data ); + ], $data, $this->user ); $this->assertEquals( 'Success', $data[0]['upload']['result'] ); $this->deleteFile( 'UploadFromUrlTest.png' ); diff --git a/tests/phpunit/integration/includes/Storage/UndoIntegrationTest.php b/tests/phpunit/integration/includes/Storage/UndoIntegrationTest.php index 6c2bfab432e3..2fe68c18ddbc 100644 --- a/tests/phpunit/integration/includes/Storage/UndoIntegrationTest.php +++ b/tests/phpunit/integration/includes/Storage/UndoIntegrationTest.php @@ -7,6 +7,7 @@ use EditPage; use FauxRequest; use McrUndoAction; use MediaWiki\Revision\RevisionStoreRecord; +use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\EditResult; use MediaWiki\Storage\SlotRecord; use MediaWikiIntegrationTestCase; @@ -419,7 +420,6 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { // after the undo, but automatic conflict resolution is not the point of // this test anyway. 'wpTextbox1' => $newContent, - 'wpEditToken' => $this->getTestSysop()->getUser()->getEditToken(), // These two parameters are the important ones here 'wpUndidRevision' => $revisionIds[$undoIndex], 'wpUndoAfter' => $revisionIds[$undoafterIndex], @@ -430,6 +430,10 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { ], true ); + $request->setVal( + 'wpEditToken', + ( new CsrfTokenSet( $request ) )->getToken()->toString() + ); $editPage = new EditPage( $article ); $editPage->importFormData( $request ); @@ -472,7 +476,6 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { [ // We emulate the user applying additional changes on top of the undo. 'wpTextbox1' => "line 1\n\nline 2\n\nline3 more content\n\neven more", - 'wpEditToken' => $this->getTestSysop()->getUser()->getEditToken(), 'wpUndidRevision' => $revisionIds[1], 'wpUndoAfter' => $revisionIds[0], 'wpStarttime' => wfTimestampNow(), @@ -482,7 +485,10 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { ], true ); - + $request->setVal( + 'wpEditToken', + ( new CsrfTokenSet( $request ) )->getToken()->toString() + ); $editPage = new EditPage( $article ); $editPage->importFormData( $request ); $editPage->internalAttemptSave( $result, false ); @@ -528,7 +534,6 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { [ // We leave the "top" content in the textbox, as the undo should have failed 'wpTextbox1' => "line 1\n\nvandalism good content\n\nline3 more content", - 'wpEditToken' => $this->getTestSysop()->getUser()->getEditToken(), 'wpUndidRevision' => $revisionIds[1], 'wpUndoAfter' => $revisionIds[0], 'wpStarttime' => wfTimestampNow(), @@ -538,6 +543,10 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { ], true ); + $request->setVal( + 'wpEditToken', + ( new CsrfTokenSet( $request ) )->getToken()->toString() + ); $editPage = new EditPage( $article ); $editPage->importFormData( $request ); diff --git a/tests/phpunit/unit/includes/Rest/Handler/ActionModuleBasedHandlerTestTrait.php b/tests/phpunit/unit/includes/Rest/Handler/ActionModuleBasedHandlerTestTrait.php index 760c5c954de3..193eb685384d 100644 --- a/tests/phpunit/unit/includes/Rest/Handler/ActionModuleBasedHandlerTestTrait.php +++ b/tests/phpunit/unit/includes/Rest/Handler/ActionModuleBasedHandlerTestTrait.php @@ -76,15 +76,18 @@ trait ActionModuleBasedHandlerTestTrait { * @return ApiMain */ private function getApiMain( $csrfSafe = false ) { + $testContext = RequestContext::getMain(); + /** @var SessionProviderInterface|MockObject $session */ $sessionProvider = $this->createNoOpMock( SessionProviderInterface::class, [ 'safeAgainstCsrf' ] ); $sessionProvider->method( 'safeAgainstCsrf' )->willReturn( $csrfSafe ); /** @var Session|MockObject $session */ - $session = $this->createNoOpMock( Session::class, [ 'getSessionId', 'getProvider' ] ); + $session = $this->createNoOpMock( Session::class, [ 'getSessionId', 'getProvider', 'getUser' ] ); $session->method( 'getSessionId' )->willReturn( new SessionId( 'test' ) ); $session->method( 'getProvider' )->willReturn( $sessionProvider ); + $session->method( 'getUser' )->willReturn( $testContext->getUser() ); // NOTE: This being a FauxRequest instance triggers special case behavior // in ApiMain, causing ApiMain::isInternalMode() to return true. Among other things, @@ -96,8 +99,6 @@ trait ActionModuleBasedHandlerTestTrait { $fauxRequest->method( 'getSession' )->willReturn( $session ); $fauxRequest->method( 'getSessionId' )->willReturn( $session->getSessionId() ); - $testContext = RequestContext::getMain(); - $fauxContext = new RequestContext(); $fauxContext->setRequest( $fauxRequest ); $fauxContext->setUser( $testContext->getUser() ); diff --git a/tests/phpunit/unit/includes/context/DerivativeContextTest.php b/tests/phpunit/unit/includes/context/DerivativeContextTest.php index 82f9493ac0f7..e37ed65a58bd 100644 --- a/tests/phpunit/unit/includes/context/DerivativeContextTest.php +++ b/tests/phpunit/unit/includes/context/DerivativeContextTest.php @@ -7,10 +7,13 @@ use FauxRequest; use HashConfig; use IContextSource; use Language; +use MediaWiki\Session\Session; +use MediaWiki\User\UserIdentityValue; use MediaWikiUnitTestCase; use OutputPage; use RequestContext; use User; +use WebRequest; /** * @coversDefaultClass DerivativeContext @@ -102,4 +105,23 @@ class DerivativeContextTest extends MediaWikiUnitTestCase { $derivativeContext->$setter( $newValue ); $this->assertSame( $newValue, $derivativeContext->$getter() ); } + + /** + * @covers ::getCsrfTokenSet + */ + public function testGetCsrfTokeSetRespectsRequest() { + $context = new RequestContext(); + $context->setRequest( $this->createNoOpMock( WebRequest::class ) ); + $derivativeContext = new DerivativeContext( $context ); + $sessionMock = $this->createNoOpMock( Session::class, [ 'getUser' ] ); + $sessionMock + ->method( 'getUser' ) + ->willReturn( UserIdentityValue::newAnonymous( '127.0.0.1' ) ); + $requestMock = $this->createNoOpMock( WebRequest::class, [ 'getSession' ] ); + $requestMock + ->method( 'getSession' ) + ->willReturn( $sessionMock ); + $derivativeContext->setRequest( $requestMock ); + $this->assertNotNull( $derivativeContext->getCsrfTokenSet()->getToken() ); + } } |