diff --git a/includes/EditPage.php b/includes/EditPage.php index a22db308648..3bdf2197441 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -55,7 +55,6 @@ 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; @@ -1627,7 +1626,9 @@ class EditPage implements IEditObject { * @internal */ public function tokenOk( &$request ) { - $this->mTokenOk = ( new CsrfTokenSet( $request ) )->matchTokenField(); + $token = $request->getVal( 'wpEditToken' ); + $user = $this->context->getUser(); + $this->mTokenOk = $user->matchEditToken( $token ); return $this->mTokenOk; } @@ -3493,10 +3494,7 @@ class EditPage implements IEditObject { */ $this->context->getOutput()->addHTML( "\n" . - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->context->getCsrfTokenSet()->getToken()->toString() - ) . + Html::hidden( "wpEditToken", $this->context->getUser()->getEditToken() ) . "\n" ); } diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php index 1e4091842dc..aaaaf728ae9 100644 --- a/includes/FileDeleteForm.php +++ b/includes/FileDeleteForm.php @@ -25,7 +25,6 @@ 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; @@ -112,6 +111,7 @@ 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,10 +130,7 @@ class FileDeleteForm { } // Perform the deletion if appropriate - if ( $request->wasPosted() && - $this->context->getCsrfTokenSet() - ->matchTokenField( CsrfTokenSet::DEFAULT_FIELD_NAME, $this->oldimage ) - ) { + if ( $request->wasPosted() && $this->context->getUser()->matchEditToken( $token, $this->oldimage ) ) { $permissionStatus = PermissionStatus::newEmpty(); if ( !$this->context->getAuthority()->authorizeWrite( 'delete', $this->title, $permissionStatus @@ -434,8 +431,8 @@ class FileDeleteForm { $fieldset, new OOUI\HtmlSnippet( Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->context->getCsrfTokenSet()->getToken( $this->oldimage )->toString() + 'wpEditToken', + $this->context->getUser()->getEditToken( $this->oldimage ) ) ) ); diff --git a/includes/Linker.php b/includes/Linker.php index ba91cee932a..b3203ef777c 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -2184,7 +2184,7 @@ class Linker { $query = [ 'action' => 'rollback', 'from' => $revUserText, - 'token' => $context->getCsrfTokenSet()->getToken( 'rollback' )->toString(), + 'token' => $context->getUser()->getEditToken( 'rollback' ), ]; $attrs = [ diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 564a17fd0e6..31cd4e20ff8 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -3481,7 +3481,7 @@ class OutputPage extends ContextSource { // Anons have predictable edit tokens return false; } - if ( !$this->getCsrfTokenSet()->matchTokenField() ) { + if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) { return false; } diff --git a/includes/ProtectionForm.php b/includes/ProtectionForm.php index ea237f8d0a1..f1abbfca3ef 100644 --- a/includes/ProtectionForm.php +++ b/includes/ProtectionForm.php @@ -28,7 +28,6 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Permissions\Authority; use MediaWiki\Permissions\PermissionManager; use MediaWiki\Permissions\PermissionStatus; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Watchlist\WatchlistManager; /** @@ -328,10 +327,11 @@ class ProtectionForm { return false; } - if ( !$this->mContext->getCsrfTokenSet()->matchTokenField( - CsrfTokenSet::DEFAULT_FIELD_NAME, - [ 'protect', $this->mTitle->getPrefixedDBkey() ] - ) ) { + $token = $this->mRequest->getVal( 'wpEditToken' ); + $legacyUser = MediaWikiServices::getInstance() + ->getUserFactory() + ->newFromAuthority( $this->mPerformer ); + if ( !$legacyUser->matchEditToken( $token, [ 'protect', $this->mTitle->getPrefixedDBkey() ] ) ) { $this->show( [ 'sessionfailure' ] ); return false; } @@ -585,13 +585,13 @@ class ProtectionForm { } if ( !$this->disabled ) { - $fields[CsrfTokenSet::DEFAULT_FIELD_NAME] = [ - 'name' => CsrfTokenSet::DEFAULT_FIELD_NAME, + $legacyUser = MediaWikiServices::getInstance() + ->getUserFactory() + ->newFromAuthority( $this->mPerformer ); + $fields['wpEditToken'] = [ + 'name' => 'wpEditToken', 'type' => 'hidden', - 'default' => $this->mContext - ->getCsrfTokenSet() - ->getToken( [ 'protect', $this->mTitle->getPrefixedDBkey() ] ) - ->toString(), + 'default' => $legacyUser->getEditToken( [ 'protect', $this->mTitle->getPrefixedDBkey() ] ), ]; } diff --git a/includes/Rest/Handler/EditHandler.php b/includes/Rest/Handler/EditHandler.php index 83a6a7ecf1a..9c28ad210b2 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->getApiMain()->getCsrfTokenSet()->getToken()->toString(); + return $this->getUser()->getEditToken(); } else { return $body['token'] ?? ''; } diff --git a/includes/actions/RollbackAction.php b/includes/actions/RollbackAction.php index e992b275c5f..b6b34d57b5e 100644 --- a/includes/actions/RollbackAction.php +++ b/includes/actions/RollbackAction.php @@ -153,7 +153,7 @@ class RollbackAction extends FormAction { ] ); } - if ( !$this->getContext()->getCsrfTokenSet()->matchTokenField( 'token', 'rollback' ) ) { + if ( !$user->matchEditToken( $request->getVal( 'token' ), 'rollback' ) ) { throw new ErrorPageError( 'sessionfailure-title', 'sessionfailure' ); } diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php index 277df154b2d..ab51c63c828 100644 --- a/includes/actions/WatchAction.php +++ b/includes/actions/WatchAction.php @@ -23,7 +23,6 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Page\PageIdentity; use MediaWiki\Permissions\Authority; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Watchlist\WatchlistManager; use Wikimedia\ParamValidator\TypeDef\ExpiryDef; @@ -346,9 +345,7 @@ class WatchAction extends FormAction { $action = 'watch'; } // This must match ApiWatch and ResourceLoaderUserOptionsModule - return ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken( $action ) - ->toString(); + return $user->getEditToken( $action ); } public function doesWrites() { diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index b9cccc0c345..92b4d26e63c 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -1097,7 +1097,11 @@ abstract class ApiBase extends ContextSource { } $webUiSalt = $this->getWebUITokenSalt( $params ); - if ( $webUiSalt !== null && $this->getCsrfTokenSet()->matchToken( $token, $webUiSalt ) ) { + if ( $webUiSalt !== null && $this->getUser()->matchEditToken( + $token, + $webUiSalt, + $this->getRequest() + ) ) { return true; } diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index 1c95c958300..a3028590959 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -26,7 +26,6 @@ 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; @@ -348,7 +347,7 @@ class ApiEditPage extends ApiBase { 'wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentModel, - CsrfTokenSet::DEFAULT_FIELD_NAME => $params['token'], + 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => true, 'wpIgnoreBlankArticle' => true, 'wpIgnoreSelfRedirect' => true, diff --git a/includes/api/ApiQueryDeletedrevs.php b/includes/api/ApiQueryDeletedrevs.php index 022b34cfbcc..043e2af4392 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 = $this->getCsrfTokenSet()->getToken()->toString(); + $token = $user->getEditToken( '', $this->getMain()->getRequest() ); } $dir = $params['dir']; diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index c6612a92b81..2efcfcce7e6 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -24,7 +24,6 @@ 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. @@ -209,9 +208,7 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['edit'] ) ) { - self::$cachedTokens['edit'] = ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken() - ->toString(); + self::$cachedTokens['edit'] = $user->getEditToken(); } return self::$cachedTokens['edit']; @@ -284,9 +281,7 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['email'] ) ) { - self::$cachedTokens['email'] = ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken() - ->toString(); + self::$cachedTokens['email'] = $user->getEditToken(); } return self::$cachedTokens['email']; @@ -304,9 +299,7 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['import'] ) ) { - self::$cachedTokens['import'] = ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken() - ->toString(); + self::$cachedTokens['import'] = $user->getEditToken(); } return self::$cachedTokens['import']; @@ -324,9 +317,7 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['watch'] ) ) { - self::$cachedTokens['watch'] = ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken( 'watch' ) - ->toString(); + self::$cachedTokens['watch'] = $user->getEditToken( 'watch' ); } return self::$cachedTokens['watch']; @@ -344,9 +335,7 @@ class ApiQueryInfo extends ApiQueryBase { // The token is always the same, let's exploit that if ( !isset( self::$cachedTokens['options'] ) ) { - self::$cachedTokens['options'] = ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken() - ->toString(); + self::$cachedTokens['options'] = $user->getEditToken(); } return self::$cachedTokens['options']; diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php index 37f16b1b704..bfcb833f07d 100644 --- a/includes/api/ApiQueryRecentChanges.php +++ b/includes/api/ApiQueryRecentChanges.php @@ -23,7 +23,6 @@ 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; @@ -127,8 +126,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase { static $cachedPatrolToken = null; if ( $cachedPatrolToken === null ) { - $cachedPatrolToken = ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken( 'patrol' )->toString(); + $cachedPatrolToken = $user->getEditToken( 'patrol' ); } return $cachedPatrolToken; diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index c5a02748c6c..612d447e53c 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -25,7 +25,6 @@ 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; @@ -120,8 +119,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase { return false; } - return ( new CsrfTokenSet( $user->getRequest() ) ) - ->getToken( 'rollback' )->toString(); + return $user->getEditToken( 'rollback' ); } protected function run( ApiPageSet $resultPageSet = null ) { diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php index 074b92e4477..9945c5d5f99 100644 --- a/includes/api/ApiQueryUserInfo.php +++ b/includes/api/ApiQueryUserInfo.php @@ -219,7 +219,7 @@ class ApiQueryUserInfo extends ApiQueryBase { !$this->lacksSameOriginSecurity() && $this->getAuthority()->isAllowed( 'editmyoptions' ) ) { - $vals['preferencestoken'] = $this->getCsrfTokenSet()->getToken()->toString(); + $vals['preferencestoken'] = $user->getEditToken( '', $this->getMain()->getRequest() ); } if ( isset( $this->prop['editcount'] ) ) { diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php index fecc56c9f55..5dd07292416 100644 --- a/includes/api/ApiQueryUsers.php +++ b/includes/api/ApiQueryUsers.php @@ -22,7 +22,6 @@ use MediaWiki\Auth\AuthManager; use MediaWiki\Block\DatabaseBlock; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserFactory; use MediaWiki\User\UserGroupManager; use MediaWiki\User\UserNameUtils; @@ -134,9 +133,7 @@ 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 ( new CsrfTokenSet( $actingUser->getRequest() ) ) - ->getToken( $targetUser->getName() ) - ->toString(); + return $actingUser->getEditToken( $targetUser->getName() ); } public function execute() { diff --git a/includes/context/DerivativeContext.php b/includes/context/DerivativeContext.php index f96e15151bc..3a90335190a 100644 --- a/includes/context/DerivativeContext.php +++ b/includes/context/DerivativeContext.php @@ -20,7 +20,6 @@ */ use MediaWiki\MediaWikiServices; use MediaWiki\Permissions\Authority; -use MediaWiki\Session\CsrfTokenSet; /** * An IContextSource implementation which will inherit context from another source @@ -288,14 +287,4 @@ 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 e0128aa7b16..7a74cbf8fb1 100644 --- a/includes/htmlform/HTMLForm.php +++ b/includes/htmlform/HTMLForm.php @@ -24,7 +24,6 @@ use MediaWiki\HookContainer\ProtectedHookAccessorTrait; use MediaWiki\Linker\LinkTarget; use MediaWiki\Page\PageReference; -use MediaWiki\Session\CsrfTokenSet; /** * Object handling generic submission, CSRF protection, layout and @@ -593,12 +592,12 @@ class HTMLForm extends ContextSource { if ( $this->getMethod() !== 'post' ) { $tokenOkay = true; // no session check needed } elseif ( $this->getRequest()->wasPosted() ) { - $editToken = $this->getRequest()->getVal( CsrfTokenSet::DEFAULT_FIELD_NAME ); + $editToken = $this->getRequest()->getVal( 'wpEditToken' ); 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->getCsrfTokenSet()->matchToken( $editToken, $this->mTokenSalt ); + $tokenOkay = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt ); } else { $tokenOkay = true; } @@ -1195,9 +1194,9 @@ class HTMLForm extends ContextSource { } if ( $this->getMethod() === 'post' ) { $html .= Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getCsrfTokenSet()->getToken( $this->mTokenSalt )->toString(), - [ 'id' => CsrfTokenSet::DEFAULT_FIELD_NAME ] + 'wpEditToken', + $this->getUser()->getEditToken( $this->mTokenSalt ), + [ 'id' => 'wpEditToken' ] ) . "\n"; $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n"; } diff --git a/includes/htmlform/HTMLFormField.php b/includes/htmlform/HTMLFormField.php index bf5782ba137..43cb401330c 100644 --- a/includes/htmlform/HTMLFormField.php +++ b/includes/htmlform/HTMLFormField.php @@ -1,7 +1,5 @@ getCheck( CsrfTokenSet::DEFAULT_FIELD_NAME ) || - $request->getCheck( 'wpFormIdentifier' ); + return $request->getCheck( 'wpEditToken' ) || $request->getCheck( 'wpFormIdentifier' ); } /** diff --git a/includes/htmlform/fields/HTMLFormFieldCloner.php b/includes/htmlform/fields/HTMLFormFieldCloner.php index b443a07836e..fba81bf23b0 100644 --- a/includes/htmlform/fields/HTMLFormFieldCloner.php +++ b/includes/htmlform/fields/HTMLFormFieldCloner.php @@ -1,7 +1,5 @@ getCheck( CsrfTokenSet::DEFAULT_FIELD_NAME ) && - $request->getArray( $this->mName ) === null - ) { + if ( !$request->getCheck( 'wpEditToken' ) && $request->getArray( $this->mName ) === null ) { return $this->getDefault(); } @@ -164,7 +160,8 @@ 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 203d34e088b..7a109e9d2ce 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -30,7 +30,6 @@ 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; @@ -1897,11 +1896,8 @@ class Article implements Page { $reason = $deleteReasonList; } - if ( $request->wasPosted() && - $this->getContext()->getCsrfTokenSet()->matchTokenField( - CsrfTokenSet::DEFAULT_FIELD_NAME, - [ 'delete', $this->getTitle()->getPrefixedText() ] - ) + if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ), + [ 'delete', $this->getTitle()->getPrefixedText() ] ) ) { # Flag to hide all contents of the archived revisions @@ -2130,13 +2126,7 @@ class Article implements Page { $form->appendContent( $fieldset, new OOUI\HtmlSnippet( - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext() - ->getCsrfTokenSet() - ->getToken( [ 'delete', $title->getPrefixedText() ] ) - ->toString() - ) + Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) ) ) ); diff --git a/includes/page/ImageHistoryList.php b/includes/page/ImageHistoryList.php index 340f4f48f3f..c686199f47e 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' => $this->getCsrfTokenSet()->getToken( $img )->toString(), + 'token' => $user->getEditToken( $img ) ] ); } else { diff --git a/includes/resourceloader/ResourceLoaderUserOptionsModule.php b/includes/resourceloader/ResourceLoaderUserOptionsModule.php index 2cacb560c9d..06496702324 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 ) { - $tokenSet = RequestContext::getMain()->getCsrfTokenSet(); + $user = $context->getUserObj(); + $tokens = [ - 'patrolToken' => $tokenSet->getToken( 'patrol' )->toString(), - 'watchToken' => $tokenSet->getToken( 'watch' )->toString(), - 'csrfToken' => $tokenSet->getToken()->toString(), + 'patrolToken' => $user->getEditToken( 'patrol' ), + 'watchToken' => $user->getEditToken( 'watch' ), + 'csrfToken' => $user->getEditToken(), ]; $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 34879caadd8..3a710e839ff 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->getCsrfTokenSet()->getToken( $key )->toString(), + 'token' => $this->list->getUser()->getEditToken( $key ) ] ); } @@ -124,7 +124,7 @@ class RevDelArchivedFileItem extends RevDelFileItem { [ 'target' => $this->list->getPageName(), 'file' => $file->getKey(), - 'token' => $this->list->getCsrfTokenSet()->getToken( $file->getKey() )->toString(), + 'token' => $user->getEditToken( $file->getKey() ) ] ), ]; diff --git a/includes/revisiondelete/RevDelFileItem.php b/includes/revisiondelete/RevDelFileItem.php index ac8d07408b4..4b51cfd9371 100644 --- a/includes/revisiondelete/RevDelFileItem.php +++ b/includes/revisiondelete/RevDelFileItem.php @@ -154,9 +154,8 @@ class RevDelFileItem extends RevDelItem { [ 'target' => $this->list->getPageName(), 'file' => $this->file->getArchiveName(), - 'token' => $this->list->getCsrfTokenSet() - ->getToken( $this->file->getArchiveName() ) - ->toString(), + 'token' => $this->list->getUser()->getEditToken( + $this->file->getArchiveName() ) ] ); } @@ -236,9 +235,7 @@ class RevDelFileItem extends RevDelItem { [ 'target' => $this->list->getPageName(), 'file' => $file->getArchiveName(), - 'token' => $this->list->getCsrfTokenSet() - ->getToken( $file->getArchiveName() ) - ->toString(), + 'token' => $user->getEditToken( $file->getArchiveName() ) ] ), ]; diff --git a/includes/search/searchwidgets/SearchFormWidget.php b/includes/search/searchwidgets/SearchFormWidget.php index 546b25743ff..d86f3e8f2ab 100644 --- a/includes/search/searchwidgets/SearchFormWidget.php +++ b/includes/search/searchwidgets/SearchFormWidget.php @@ -326,13 +326,10 @@ 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' => $this->specialSearch - ->getContext() - ->getCsrfTokenSet() - ->getToken( 'searchnamespace' ) - ->toString(), - ] + [ 'value' => $user->getEditToken( + 'searchnamespace', + $this->specialSearch->getRequest() + ) ] ); } diff --git a/includes/specials/SpecialEditTags.php b/includes/specials/SpecialEditTags.php index 732c67e366c..65b94d4534d 100644 --- a/includes/specials/SpecialEditTags.php +++ b/includes/specials/SpecialEditTags.php @@ -20,7 +20,6 @@ */ use MediaWiki\Permissions\PermissionManager; -use MediaWiki\Session\CsrfTokenSet; /** * Special page for adding and removing change tags to individual revisions. @@ -289,13 +288,7 @@ class SpecialEditTags extends UnlistedSpecialPage { '' . "\n" . Xml::closeElement( 'table' ) . - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext() - ->getCsrfTokenSet() - ->getToken() - ->toString() - ) . + Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) . Html::hidden( 'target', $this->targetObj->getPrefixedText() ) . Html::hidden( 'type', $this->typeName ) . Html::hidden( 'ids', implode( ',', $this->ids ) ) . @@ -407,7 +400,8 @@ class SpecialEditTags extends UnlistedSpecialPage { protected function submit() { // Check edit token on submission $request = $this->getRequest(); - if ( $this->submitClicked && !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { + $token = $request->getVal( 'wpEditToken' ); + if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) { $this->getOutput()->addWikiMsg( 'sessionfailure' ); return false; } diff --git a/includes/specials/SpecialEmailUser.php b/includes/specials/SpecialEmailUser.php index 6144df98453..d22f129236e 100644 --- a/includes/specials/SpecialEmailUser.php +++ b/includes/specials/SpecialEmailUser.php @@ -23,7 +23,6 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Preferences\MultiUsernameFilter; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserNamePrefixSearch; use MediaWiki\User\UserNameUtils; use MediaWiki\User\UserOptionsLookup; @@ -147,7 +146,7 @@ class SpecialEmailUser extends UnlistedSpecialPage { // error out if sending user cannot do this $error = self::getPermissionsError( $this->getUser(), - $this->getRequest()->getVal( CsrfTokenSet::DEFAULT_FIELD_NAME ), + $this->getRequest()->getVal( 'wpEditToken' ), $this->getConfig() ); diff --git a/includes/specials/SpecialExpandTemplates.php b/includes/specials/SpecialExpandTemplates.php index 5b6bbd4443b..2c13058ac51 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 ( !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { + } elseif ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ), '', $request ) ) { $error = [ 'expand_templates_preview_fail_html' ]; } else { $error = false; diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index 0e7a2e285aa..7d7fafdc216 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 ( !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { + if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) { $source = Status::newFatal( 'import-token-mismatch' ); } elseif ( $sourceName === 'upload' ) { $isUpload = true; diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php index 67aed2520e6..a683e6f144e 100644 --- a/includes/specials/SpecialMergeHistory.php +++ b/includes/specials/SpecialMergeHistory.php @@ -25,7 +25,6 @@ use MediaWiki\Cache\LinkBatchFactory; use MediaWiki\Page\MergeHistoryFactory; use MediaWiki\Revision\RevisionRecord; use MediaWiki\Revision\RevisionStore; -use MediaWiki\Session\CsrfTokenSet; use Wikimedia\Rdbms\ILoadBalancer; /** @@ -125,7 +124,7 @@ class SpecialMergeHistory extends SpecialPage { $this->mComment = $request->getText( 'wpComment' ); $this->mMerge = $request->wasPosted() - && $this->getContext()->getCsrfTokenSet()->matchTokenField(); + && $this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) ); // target page if ( $this->mSubmitted ) { @@ -307,10 +306,7 @@ 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( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext()->getCsrfTokenSet()->getToken()->toString() - ); + $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); $misc .= Xml::closeElement( 'form' ); $out->addHTML( $misc ); diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php index 395c82ffb9b..52679002156 100644 --- a/includes/specials/SpecialMovepage.php +++ b/includes/specials/SpecialMovepage.php @@ -27,7 +27,6 @@ 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; @@ -207,7 +206,7 @@ class MovePageForm extends UnlistedSpecialPage { $this->watch = $request->getCheck( 'wpWatch' ) && $user->isRegistered(); if ( $request->getVal( 'action' ) == 'submit' && $request->wasPosted() - && $this->getContext()->getCsrfTokenSet()->matchTokenField() + && $user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) { $this->doSubmit(); } else { @@ -597,10 +596,7 @@ class MovePageForm extends UnlistedSpecialPage { new OOUI\HtmlSnippet( $hiddenFields . Html::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) . - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext()->getCsrfTokenSet()->getToken()->toString() - ) + Html::hidden( 'wpEditToken', $user->getEditToken() ) ) ); diff --git a/includes/specials/SpecialRevisionDelete.php b/includes/specials/SpecialRevisionDelete.php index 1da6b275efe..efb0f04dc01 100644 --- a/includes/specials/SpecialRevisionDelete.php +++ b/includes/specials/SpecialRevisionDelete.php @@ -23,7 +23,6 @@ use MediaWiki\Permissions\PermissionManager; use MediaWiki\Revision\RevisionRecord; -use MediaWiki\Session\CsrfTokenSet; /** * Special page allowing users with the appropriate permissions to view @@ -44,6 +43,9 @@ 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; @@ -159,6 +161,7 @@ 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 ); @@ -340,7 +343,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { throw new PermissionsError( 'deletedtext' ); } } - if ( !$this->getContext()->getCsrfTokenSet()->matchTokenField( 'token', $archiveName ) ) { + if ( !$user->matchEditToken( $this->token, $archiveName ) ) { $lang = $this->getLanguage(); $this->getOutput()->addWikiMsg( 'revdelete-show-file-confirm', $this->targetObj->getText(), @@ -352,10 +355,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { 'action' => $this->getPageTitle()->getLocalURL( [ 'target' => $this->targetObj->getPrefixedDBkey(), 'file' => $archiveName, - 'token' => $this->getContext() - ->getCsrfTokenSet() - ->getToken( $archiveName ) - ->toString(), + 'token' => $user->getEditToken( $archiveName ), ] ) ] ) . @@ -494,10 +494,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { '' . "\n" . Xml::closeElement( 'table' ) . - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext()->getCsrfTokenSet()->getToken()->toString() - ) . + Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) . Html::hidden( 'target', $this->targetObj->getPrefixedText() ) . Html::hidden( 'type', $this->typeName ) . Html::hidden( 'ids', implode( ',', $this->ids ) ) . @@ -627,7 +624,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { */ protected function submit() { # Check edit token on submission - if ( $this->submitClicked && !$this->getContext()->getCsrfTokenSet()->matchTokenField() ) { + $token = $this->getRequest()->getVal( 'wpEditToken' ); + if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) { $this->getOutput()->addWikiMsg( 'sessionfailure' ); return false; diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index f6a5b7912d8..9e350966d51 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -730,8 +730,11 @@ class SpecialSearch extends SpecialPage { $request = $this->getRequest(); if ( $user->isRegistered() && - $this->getContext()->getCsrfTokenSet()->matchTokenField( 'nsRemember', 'searchnamespace' ) && - !$this->readOnlyMode->isReadOnly() + $user->matchEditToken( + $request->getVal( 'nsRemember' ), + 'searchnamespace', + $request + ) && !$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 5276049b13a..fdc2a41e289 100644 --- a/includes/specials/SpecialTags.php +++ b/includes/specials/SpecialTags.php @@ -21,8 +21,6 @@ * @ingroup SpecialPage */ -use MediaWiki\Session\CsrfTokenSet; - /** * A special page that lists tags for edits * @@ -316,7 +314,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( CsrfTokenSet::DEFAULT_FIELD_NAME ); + $context->getRequest()->unsetVal( 'wpEditToken' ); $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 bc116da0954..7f38bb8d6e9 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -29,7 +29,6 @@ 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; @@ -172,7 +171,8 @@ class SpecialUndelete extends SpecialPage { $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : ''; $this->mFilename = $request->getVal( 'file' ); - $posted = $request->wasPosted() && $this->getContext()->getCsrfTokenSet()->matchTokenField(); + $posted = $request->wasPosted() && + $user->matchEditToken( $request->getVal( 'wpEditToken' ) ); $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 ( !$this->getContext()->getCsrfTokenSet()->matchToken( $this->mToken, $this->mFilename ) ) { + } elseif ( !$user->matchEditToken( $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' => CsrfTokenSet::DEFAULT_FIELD_NAME, - 'value' => $this->getContext()->getCsrfTokenSet()->getToken()->toString() ] ) . + 'name' => 'wpEditToken', + 'value' => $user->getEditToken() ] ) . 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' => $this->getContext()->getCsrfTokenSet()->getToken()->toString(), + 'token' => $user->getEditToken( $key ), ] ), ] ) . @@ -988,9 +988,7 @@ class SpecialUndelete extends SpecialPage { ] ), new OOUI\HtmlSnippet( Html::hidden( 'target', $this->mTarget ) . - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext()->getCsrfTokenSet()->getToken()->toString() ) + Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) ) ); } @@ -1041,10 +1039,7 @@ class SpecialUndelete extends SpecialPage { if ( $this->mAllowed ) { # Slip in the hidden controls here $misc = Html::hidden( 'target', $this->mTarget ); - $misc .= Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext()->getCsrfTokenSet()->getToken()->toString() - ); + $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); $history .= $misc; $form->appendContent( new OOUI\HtmlSnippet( $history ) ); @@ -1256,7 +1251,7 @@ class SpecialUndelete extends SpecialPage { [ 'target' => $this->mTargetObj->getPrefixedText(), 'file' => $key, - 'token' => $this->getContext()->getCsrfTokenSet()->getToken( $key )->toString(), + 'token' => $user->getEditToken( $key ) ] ); diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php index 909052dd325..e1abe163a64 100644 --- a/includes/specials/SpecialUpload.php +++ b/includes/specials/SpecialUpload.php @@ -156,7 +156,8 @@ 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) - $this->mTokenOk = $this->getContext()->getCsrfTokenSet()->matchTokenField(); + $token = $request->getVal( 'wpEditToken' ); + $this->mTokenOk = $this->getUser()->matchEditToken( $token ); $this->uploadFormTextTop = ''; $this->uploadFormTextAfterSummary = ''; diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php index a8f7d1cdfea..506611a268c 100644 --- a/includes/specials/SpecialUserrights.php +++ b/includes/specials/SpecialUserrights.php @@ -22,7 +22,6 @@ */ use MediaWiki\MediaWikiServices; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\User\UserGroupManager; use MediaWiki\User\UserGroupManagerFactory; use MediaWiki\User\UserIdentity; @@ -188,10 +187,7 @@ class UserrightsPage extends SpecialPage { $request->wasPosted() && $request->getCheck( 'saveusergroups' ) && $this->mTarget !== null && - $this->getContext()->getCsrfTokenSet()->matchTokenField( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->mTarget - ) + $user->matchEditToken( $request->getVal( 'wpEditToken' ), $this->mTarget ) ) { /* * If the user is blocked and they only have "partial" access @@ -775,10 +771,7 @@ class UserrightsPage extends SpecialPage { ] ) . Html::hidden( 'user', $this->mTarget ) . - Html::hidden( - CsrfTokenSet::DEFAULT_FIELD_NAME, - $this->getContext()->getCsrfTokenSet()->getToken( $this->mTarget )->toString() - ) . + Html::hidden( 'wpEditToken', $this->getUser()->getEditToken( $this->mTarget ) ) . Html::hidden( 'conflictcheck-originalgroups', implode( ',', $user->getGroups() ) diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index af53ee03c8f..16c54d0858d 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() - && $this->getContext()->getCsrfTokenSet()->matchTokenField( 'token' ) + && $user->matchEditToken( $request->getVal( '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', $this->getContext()->getCsrfTokenSet()->getToken()->toString() ) . "\n" . + Html::hidden( 'token', $user->getEditToken() ) . "\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 bbd89430f18..7e05d836229 100644 --- a/tests/phpunit/includes/EditPageConstraintsTest.php +++ b/tests/phpunit/includes/EditPageConstraintsTest.php @@ -2,7 +2,6 @@ use MediaWiki\EditPage\SpamChecker; use MediaWiki\Permissions\PermissionManager; -use MediaWiki\Session\CsrfTokenSet; /** * Integration tests for the various edit constraints, ensuring @@ -103,6 +102,14 @@ 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(); } @@ -112,12 +119,7 @@ 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 d754d89049d..81a478eeb36 100644 --- a/tests/phpunit/includes/EditPageTest.php +++ b/tests/phpunit/includes/EditPageTest.php @@ -2,7 +2,6 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Revision\RevisionRecord; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\EditResult; use MediaWiki\User\UserIdentity; use Wikimedia\TestingAccessWrapper; @@ -158,6 +157,10 @@ 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() : ''; } @@ -171,12 +174,6 @@ 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 ); @@ -784,8 +781,11 @@ 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,8 +810,11 @@ hello } } ); + $user = $this->getTestUser()->getUser(); + $status = $this->doEditDummyNonTextPage( [ 'wpTextbox1' => 'some text', + 'wpEditToken' => $user->getEditToken(), 'wpEdittime' => '', 'editRevId' => 0, 'wpStarttime' => wfTimestampNow(), @@ -834,8 +837,11 @@ hello } } ); + $user = $this->getTestUser()->getUser(); + $status = $this->doEditDummyNonTextPage( [ 'wpTextbox1' => 'some text', + 'wpEditToken' => $user->getEditToken(), 'wpEdittime' => '', 'editRevId' => 0, 'wpStarttime' => wfTimestampNow(), @@ -857,10 +863,6 @@ 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 c57cb473c02..da0b0d9b7de 100644 --- a/tests/phpunit/includes/OutputPageTest.php +++ b/tests/phpunit/includes/OutputPageTest.php @@ -8,6 +8,7 @@ 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; @@ -3205,40 +3206,64 @@ 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->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), - 'matchToken' => true, + 'performer' => $this->mockUserAuthorityWithPermissions( + $this->mockUser( true, true ), + [ 'edit' ] + ), 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), true ]; yield 'get request' => [ - 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), - 'matchToken' => true, + 'performer' => $this->mockUserAuthorityWithPermissions( + $this->mockUser( true, true ), + [ 'edit' ] + ), 'request' => new FauxRequest( [ 'action' => 'submit' ], false ), false ]; yield 'not a submit action' => [ - 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), - 'matchToken' => true, + 'performer' => $this->mockUserAuthorityWithPermissions( + $this->mockUser( true, true ), + [ 'edit' ] + ), 'request' => new FauxRequest( [ 'action' => 'something' ], true ), false ]; yield 'anon can not' => [ - 'performer' => $this->mockAnonAuthorityWithPermissions( [ 'edit' ] ), - 'matchToken' => true, + 'performer' => $this->mockUserAuthorityWithPermissions( + $this->mockUser( false, true ), + [ 'edit' ] + ), 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), false ]; yield 'token not match' => [ - 'performer' => $this->mockRegisteredAuthorityWithPermissions( [ 'edit' ] ), - 'matchToken' => false, + 'performer' => $this->mockUserAuthorityWithPermissions( + $this->mockUser( true, false ), + [ 'edit' ] + ), 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), false ]; yield 'no permission' => [ - 'performer' => $this->mockRegisteredAuthorityWithoutPermissions( [ 'edit' ] ), - 'matchToken' => true, + 'performer' => $this->mockUserAuthorityWithoutPermissions( + $this->mockUser( true, true ), + [ 'edit' ] + ), 'request' => new FauxRequest( [ 'action' => 'submit' ], true ), false ]; @@ -3248,9 +3273,7 @@ class OutputPageTest extends MediaWikiIntegrationTestCase { * @dataProvider provideUserCanPreview * @covers OutputPage::userCanPreview */ - 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' ); + public function testUserCanPreview( Authority $performer, WebRequest $request, bool $expected ) { $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 af884aa28a5..16a962009a1 100644 --- a/tests/phpunit/includes/actions/RollbackActionTest.php +++ b/tests/phpunit/includes/actions/RollbackActionTest.php @@ -6,7 +6,6 @@ use Article; use DerivativeContext; use ErrorPageError; use FauxRequest; -use MediaWiki\Session\CsrfTokenSet; use MediaWikiIntegrationTestCase; use RequestContext; use RollbackAction; @@ -44,13 +43,6 @@ 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 ); $mwServices = $this->getServiceContainer(); @@ -110,6 +102,7 @@ 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(); @@ -133,6 +126,7 @@ 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 f3889779ce6..a3503a281f5 100644 --- a/tests/phpunit/includes/actions/WatchActionTest.php +++ b/tests/phpunit/includes/actions/WatchActionTest.php @@ -453,8 +453,8 @@ class WatchActionTest extends MediaWikiIntegrationTestCase { $this->hideDeprecated( 'WatchAction::getWatchToken' ); $user = $this->createMock( User::class ); $user->expects( $this->once() ) - ->method( 'getRequest' ) - ->willReturn( new FauxRequest() ); + ->method( 'getEditToken' ) + ->with( 'watch' ); WatchAction::getWatchToken( $this->watchAction->getTitle(), $user, 'INVALID_ACTION' ); } @@ -466,9 +466,7 @@ class WatchActionTest extends MediaWikiIntegrationTestCase { public function testGetWatchTokenProxiesUserGetEditToken() { $this->hideDeprecated( 'WatchAction::getWatchToken' ); $user = $this->createMock( User::class ); - $user->expects( $this->once() ) - ->method( 'getRequest' ) - ->willReturn( new FauxRequest() ); + $user->expects( $this->once() )->method( 'getEditToken' ); WatchAction::getWatchToken( $this->watchAction->getTitle(), $user ); } diff --git a/tests/phpunit/includes/api/ApiDeleteTest.php b/tests/phpunit/includes/api/ApiDeleteTest.php index 2fd848dde16..f9908669341 100644 --- a/tests/phpunit/includes/api/ApiDeleteTest.php +++ b/tests/phpunit/includes/api/ApiDeleteTest.php @@ -98,10 +98,11 @@ class ApiDeleteTest extends ApiTestCase { // test deletion without permission try { $user = new User(); - $apiResult = $this->doApiRequestWithToken( [ + $apiResult = $this->doApiRequest( [ 'action' => 'delete', 'title' => $name, - ], null, $user ); + 'token' => $user->getEditToken(), + ], null, 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 48da6ba0b99..114d201a69d 100644 --- a/tests/phpunit/includes/api/ApiLogoutTest.php +++ b/tests/phpunit/includes/api/ApiLogoutTest.php @@ -46,7 +46,8 @@ class ApiLogoutTest extends ApiTestCase { $user = $this->getTestSysop()->getUser(); $this->assertTrue( $user->isRegistered(), 'sanity check' ); - $token = $wgRequest->getSession()->getToken( 'logoutToken' )->toString(); + // Logic copied from SkinTemplate. + $token = $user->getEditToken( 'logoutToken', $wgRequest ); $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 42008bdab24..6bcb4cda272 100644 --- a/tests/phpunit/includes/api/ApiUserrightsTest.php +++ b/tests/phpunit/includes/api/ApiUserrightsTest.php @@ -2,7 +2,6 @@ use MediaWiki\Block\DatabaseBlock; use MediaWiki\MediaWikiServices; -use MediaWiki\Session\CsrfTokenSet; /** * @group API @@ -248,11 +247,14 @@ class ApiUserrightsTest extends ApiTestCase { $sysop = $this->getTestSysop()->getUser(); $user = $this->getMutableTestUser()->getUser(); - $res = $this->doApiRequestWithToken( [ + $token = $sysop->getEditToken( $user->getName() ); + + $res = $this->doApiRequest( [ 'action' => 'userrights', 'user' => $user->getName(), 'add' => 'sysop', - ], null, $sysop ); + 'token' => $token, + ] ); $user->clearInstanceCache(); $this->assertSame( [ 'sysop' ], $this->getServiceContainer()->getUserGroupManager()->getUserGroups( $user ) ); @@ -270,17 +272,17 @@ class ApiUserrightsTest extends ApiTestCase { * @return ApiUserrights */ private function getMockForProcessingExpiries( $canProcessExpiries ) { + $sysop = $this->getTestSysop()->getUser(); $user = $this->getMutableTestUser()->getUser(); - $request = new FauxRequest( [ + + $token = $sysop->getEditToken( 'userrights' ); + + $main = new ApiMain( new FauxRequest( [ 'action' => 'userrights', 'user' => $user->getName(), 'add' => 'sysop', - ] ); - $request->setVal( - 'token', - ( new CsrfTokenSet( $request ) )->getToken( 'userrights' )->toString() - ); - $main = new ApiMain( $request ); + 'token' => $token, + ] ) ); $mockUserRightsPage = $this->getMockBuilder( UserrightsPage::class ) ->onlyMethods( [ 'canProcessExpiries' ] ) diff --git a/tests/phpunit/includes/api/ApiWatchTest.php b/tests/phpunit/includes/api/ApiWatchTest.php index 381b130a2a3..7f31b61e10e 100644 --- a/tests/phpunit/includes/api/ApiWatchTest.php +++ b/tests/phpunit/includes/api/ApiWatchTest.php @@ -235,12 +235,13 @@ class ApiWatchTest extends ApiTestCase { // This (and assertTrue below) are mostly for completeness. $this->assertFalse( $watchlistManager->isWatched( $contextUser, $title ) ); - $data = $this->doApiRequestWithToken( [ + $data = $this->doApiRequest( [ '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 650fdf494a5..887d11d43dc 100644 --- a/tests/phpunit/includes/specials/SpecialPageExecutor.php +++ b/tests/phpunit/includes/specials/SpecialPageExecutor.php @@ -92,13 +92,11 @@ 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->getCsrfTokenSet()->getToken()->toString() - ); + $request->setVal( 'wpEditToken', $context->getUser()->getEditToken() ); } } diff --git a/tests/phpunit/includes/upload/UploadFromUrlTest.php b/tests/phpunit/includes/upload/UploadFromUrlTest.php index 0c2ae3b1fea..cd82acff53f 100644 --- a/tests/phpunit/includes/upload/UploadFromUrlTest.php +++ b/tests/phpunit/includes/upload/UploadFromUrlTest.php @@ -105,6 +105,7 @@ class UploadFromUrlTest extends ApiTestCase { * @depends testClearQueue */ public function testSetupUrlDownload( $data ) { + $token = $this->user->getEditToken(); $exception = false; try { @@ -119,9 +120,10 @@ class UploadFromUrlTest extends ApiTestCase { $exception = false; try { - $this->doApiRequestWithToken( [ + $this->doApiRequest( [ 'action' => 'upload', - ], $data, $this->user ); + 'token' => $token, + ], $data ); } catch ( ApiUsageException $e ) { $exception = true; $this->assertEquals( 'One of the parameters "filekey", "file" and "url" is required.', @@ -131,10 +133,11 @@ class UploadFromUrlTest extends ApiTestCase { $exception = false; try { - $this->doApiRequestWithToken( [ + $this->doApiRequest( [ 'action' => 'upload', 'url' => 'http://www.example.com/test.png', - ], $data, $this->user ); + 'token' => $token, + ], $data ); } catch ( ApiUsageException $e ) { $exception = true; $this->assertEquals( 'The "filename" parameter must be set.', $e->getMessage() ); @@ -144,11 +147,12 @@ class UploadFromUrlTest extends ApiTestCase { $this->getServiceContainer()->getUserGroupManager()->removeUserFromGroup( $this->user, 'sysop' ); $exception = false; try { - $this->doApiRequestWithToken( [ + $this->doApiRequest( [ 'action' => 'upload', 'url' => 'http://www.example.com/test.png', 'filename' => 'UploadFromUrlTest.png', - ], $data, $this->user ); + 'token' => $token, + ], $data ); } catch ( ApiUsageException $e ) { $exception = true; // Two error messages are possible depending on the number of groups in the wiki with upload rights: @@ -185,7 +189,7 @@ class UploadFromUrlTest extends ApiTestCase { 'filename' => 'UploadFromUrlTest.png', 'url' => 'http://upload.wikimedia.org/wikipedia/mediawiki/b/bc/Wiki.png', 'ignorewarnings' => true, - ], $data, $this->user ); + ], $data ); $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 06878fbd860..19c978e9e71 100644 --- a/tests/phpunit/integration/includes/Storage/UndoIntegrationTest.php +++ b/tests/phpunit/integration/includes/Storage/UndoIntegrationTest.php @@ -7,7 +7,6 @@ use EditPage; use FauxRequest; use McrUndoAction; use MediaWiki\Revision\RevisionStoreRecord; -use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Storage\EditResult; use MediaWiki\Storage\SlotRecord; use MediaWikiIntegrationTestCase; @@ -422,6 +421,7 @@ 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], @@ -432,10 +432,6 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { ], true ); - $request->setVal( - 'wpEditToken', - ( new CsrfTokenSet( $request ) )->getToken()->toString() - ); $editPage = new EditPage( $article ); $editPage->importFormData( $request ); @@ -478,6 +474,7 @@ 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(), @@ -487,10 +484,7 @@ class UndoIntegrationTest extends MediaWikiIntegrationTestCase { ], true ); - $request->setVal( - 'wpEditToken', - ( new CsrfTokenSet( $request ) )->getToken()->toString() - ); + $editPage = new EditPage( $article ); $editPage->importFormData( $request ); $editPage->internalAttemptSave( $result, false ); @@ -536,6 +530,7 @@ 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(), @@ -545,10 +540,6 @@ 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 193eb685384..760c5c954de 100644 --- a/tests/phpunit/unit/includes/Rest/Handler/ActionModuleBasedHandlerTestTrait.php +++ b/tests/phpunit/unit/includes/Rest/Handler/ActionModuleBasedHandlerTestTrait.php @@ -76,18 +76,15 @@ 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', 'getUser' ] ); + $session = $this->createNoOpMock( Session::class, [ 'getSessionId', 'getProvider' ] ); $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, @@ -99,6 +96,8 @@ 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 e37ed65a58b..82f9493ac0f 100644 --- a/tests/phpunit/unit/includes/context/DerivativeContextTest.php +++ b/tests/phpunit/unit/includes/context/DerivativeContextTest.php @@ -7,13 +7,10 @@ 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 @@ -105,23 +102,4 @@ 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() ); - } }