wiki.techinc.nl/tests/phpunit/includes/page/UndeletePageTest.php
Dreamy Jazz 400c2a62ee Allow undeletion of IP revisions when temp accounts are enabled
Why:
* When wgAutoCreateTempUser['enabled'] is true, calling ActorStore
  ::acquireActorId for an IP address user will throw a
  CannotCreateActorException to prevent attributing actions to IP
  addresses.
* When using Special:Undelete to undelete a page that has IP
  addresses who have made edits, the UndeletePage call to the
  ActorStore::acquireActorId causes an exception.
* However, the revisions already existed so (similar to importing
  revisions) it does not represent a leak of the IP address.
* As such, it should be possible to undelete a page with revisions
  performed by IP addresses.

What:
* Add ActorStore::getActorStoreForUndelete which works the same
  as ActorStore::getActorStoreForImport. Having methods that
  are named for undelete / import avoids the usage for other
  code that should use ::getActorStore instead.
* Add RevisionStoreFactory::getRevisionStoreForUndelete which
  uses the ActorStore returned by ActorStore
  ::getActorStoreForUndelete.
* Update the PageCommandFactory to take the RevisionStoreFactory
  and then call ::getRevisionStore for all but UndeletePage where
  the ::getRevisionStoreForUndelete method is called.

Bug: T362019
Bug: T365669
Change-Id: Ia7c583c625843f4f400e1c4aa7ea360519e63c87
2024-05-23 09:30:48 +00:00

129 lines
4.2 KiB
PHP

<?php
use MediaWiki\CommentStore\CommentStoreComment;
use MediaWiki\Page\UndeletePage;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
use MediaWiki\Title\Title;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\IPUtils;
/**
* @group Database
* @coversDefaultClass \MediaWiki\Page\UndeletePage
*/
class UndeletePageTest extends MediaWikiIntegrationTestCase {
use TempUserTestTrait;
/**
* @var array
*/
private $pages = [];
/**
* A logged out user who edited the page before it was archived.
* @var string
*/
private $ipEditor;
protected function setUp(): void {
parent::setUp();
$this->ipEditor = '2001:DB8:0:0:0:0:0:1';
$this->setupPage( 'UndeletePageTest_thePage', NS_MAIN, ' ' );
$this->setupPage( 'UndeletePageTest_thePage', NS_TALK, ' ' );
}
/**
* @param string $titleText
* @param int $ns
* @param string $content
*/
private function setupPage( string $titleText, int $ns, string $content ): void {
$this->disableAutoCreateTempUser();
$title = Title::makeTitle( $ns, $titleText );
$page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $title );
$performer = static::getTestUser()->getUser();
$content = ContentHandler::makeContent( $content, $page->getTitle(), CONTENT_MODEL_WIKITEXT );
$updater = $page->newPageUpdater( UserIdentityValue::newAnonymous( $this->ipEditor ) )
->setContent( SlotRecord::MAIN, $content );
$revisionRecord = $updater->saveRevision( CommentStoreComment::newUnsavedComment( "testing" ) );
if ( !$updater->wasSuccessful() ) {
$this->fail( $updater->getStatus()->getWikiText() );
}
$this->pages[] = [ 'page' => $page, 'revId' => $revisionRecord->getId() ];
$this->deletePage( $page, '', $performer );
}
/**
* @covers ::undeleteUnsafe
* @covers ::undeleteRevisions
* @covers \MediaWiki\Revision\RevisionStoreFactory::getRevisionStoreForUndelete
* @covers \MediaWiki\User\ActorStoreFactory::getActorStoreForUndelete
*/
public function testUndeleteRevisions() {
// TODO: MCR: Test undeletion with multiple slots. Check that slots remain untouched.
$revisionStore = $this->getServiceContainer()->getRevisionStore();
// First make sure old revisions are archived
$dbr = $this->getDb();
foreach ( [ 0, 1 ] as $key ) {
$row = $revisionStore->newArchiveSelectQueryBuilder( $dbr )
->joinComment()
->where( [ 'ar_rev_id' => $this->pages[$key]['revId'] ] )
->caller( __METHOD__ )->fetchRow();
$this->assertEquals( $this->ipEditor, $row->ar_user_text );
// Should not be in revision
$row = $dbr->newSelectQueryBuilder()
->select( '1' )
->from( 'revision' )
->where( [ 'rev_id' => $this->pages[$key]['revId'] ] )
->fetchRow();
$this->assertFalse( $row );
// Should not be in ip_changes
$row = $dbr->newSelectQueryBuilder()
->select( '1' )
->from( 'ip_changes' )
->where( [ 'ipc_rev_id' => $this->pages[$key]['revId'] ] )
->fetchRow();
$this->assertFalse( $row );
}
// Enable autocreation of temporary users to test that undeletion of revisions performed by IP addresses works
// when temporary accounts are enabled.
$this->enableAutoCreateTempUser();
// Restore the page
$undeletePage = $this->getServiceContainer()->getUndeletePageFactory()->newUndeletePage(
$this->pages[0]['page'],
$this->getTestSysop()->getUser()
);
$status = $undeletePage->setUndeleteAssociatedTalk( true )->undeleteUnsafe( '' );
$this->assertEquals( 2, $status->value[UndeletePage::REVISIONS_RESTORED] );
// check subject page and talk page are both back in the revision table
foreach ( [ 0, 1 ] as $key ) {
$row = $revisionStore->newSelectQueryBuilder( $dbr )
->where( [ 'rev_id' => $this->pages[$key]['revId'] ] )
->caller( __METHOD__ )->fetchRow();
$this->assertNotFalse( $row, 'row exists in revision table' );
$this->assertEquals( $this->ipEditor, $row->rev_user_text );
// Should be back in ip_changes
$row = $dbr->newSelectQueryBuilder()
->select( [ 'ipc_hex' ] )
->from( 'ip_changes' )
->where( [ 'ipc_rev_id' => $this->pages[$key]['revId'] ] )
->fetchRow();
$this->assertNotFalse( $row, 'row exists in ip_changes table' );
$this->assertEquals( IPUtils::toHex( $this->ipEditor ), $row->ipc_hex );
}
}
}