Page MenuHomeWickedGov Phorge

LocalRenameJob.php
No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None

LocalRenameJob.php

<?php
namespace MediaWiki\Extension\CentralAuth\GlobalRename\LocalRenameJob;
use Exception;
use Job;
use MediaWiki\Context\RequestContext;
use MediaWiki\Extension\CentralAuth\CentralAuthServices;
use MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameUserStatus;
use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MediaWiki\WikiMap\WikiMap;
use Wikimedia\Rdbms\IDBAccessObject;
use Wikimedia\ScopedCallback;
/**
* Base class for jobs that change a user's
* name. Intended to be run on local wikis
* indvidually.
*
* Parameters:
* - from: current username
* - to: new username to rename to
* - renamer: username of the performer
* - ignorestatus: when true, the rename will be done even if another job is supposed to be
* already doing it. This should only be used for stuck renames.
* - session: array of session data from RequestContext::exportSession()
*/
abstract class LocalRenameJob extends Job {
/**
* @var GlobalRenameUserStatus
*/
private $renameuserStatus;
/**
* @param Title $title
* @param array $params
*/
public function __construct( Title $title, $params ) {
parent::__construct( $this->command, $title, $params );
}
/**
* @throws Exception
*/
public function run(): bool {
$this->setRenameUserStatus(
CentralAuthServices::getGlobalRenameFactory()
->newGlobalRenameUserStatus( $this->params['to'] )
);
// Bail if it's already done or in progress. Use a locking read to block until the
// transaction adding this job is done, so we can see its changes. This is similar to
// the trick that the RenameUser extension does.
$status = $this->renameuserStatus->getStatus( IDBAccessObject::READ_LOCKING );
// Clear any REPEATABLE-READ snapshot in case the READ_LOCKING blocked above. We want
// regular non-locking SELECTs to see all the changes from that transaction we waited on.
// Making a new transaction also reduces deadlocks from the locking read.
// T145596
$fnameTrxOwner = get_class( $this ) . '::' . __FUNCTION__;
$factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$factory->commitPrimaryChanges( $fnameTrxOwner );
if ( empty( $this->params['ignorestatus'] ) ) {
if ( $status !== 'queued' && $status !== 'failed' ) {
$logger = LoggerFactory::getInstance( 'CentralAuth' );
$logger->info( 'Skipping duplicate rename from {oldName} to {newName}', [
'oldName' => $this->params['from'],
'newName' => $this->params['to'],
'component' => 'GlobalRename',
'status' => $status,
] );
return true;
}
}
if ( isset( $this->params['session'] ) ) {
// Don't carry over users or sessions because it's going to be wrong
// across wikis
$this->params['session']['userId'] = 0;
$this->params['session']['sessionId'] = '';
$callback = RequestContext::importScopedSession( $this->params['session'] );
$this->addTeardownCallback( static function () use ( &$callback ) {
ScopedCallback::consume( $callback );
} );
}
try {
$this->doRun( $fnameTrxOwner );
$this->addTeardownCallback( [ $this, 'scheduleNextWiki' ] );
} catch ( Exception $e ) {
// This will lock the user out of their account until a sysadmin intervenes
$factory->rollbackPrimaryChanges( $fnameTrxOwner );
$this->updateStatus( 'failed' );
$factory->commitPrimaryChanges( $fnameTrxOwner );
// Record job failure in CentralAuth channel (T217211)
$logger = LoggerFactory::getInstance( 'CentralAuth' );
$logger->error( 'Failed to rename {oldName} to {newName} ({error})', [
'oldName' => $this->params['from'],
'newName' => $this->params['to'],
'component' => 'GlobalRename',
'error' => $e->getMessage()
] );
throw $e;
}
return true;
}
/**
* Actually do the work for the job class
* @param string $fnameTrxOwner Caller name
*/
abstract protected function doRun( $fnameTrxOwner );
/**
* @param GlobalRenameUserStatus $status
*/
protected function setRenameUserStatus( GlobalRenameUserStatus $status ) {
$this->renameuserStatus = $status;
}
/**
* Get the user object for the user who is doing the renaming
* "Auto-create" if it doesn't exist yet.
* @return User
*/
protected function getRenameUser() {
$user = User::newFromName( $this->params['renamer'] );
$userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils();
// If the username is a reserved name, don't worry about the account
// existing, just use it.
if ( !$userNameUtils->isUsable( $user->getName() ) ) {
return $user;
}
$caUser = CentralAuthUser::getPrimaryInstance( $user );
// Race condition where the renamer isn't attached here, but
// someone creates an account in the meantime and then bad
// stuff could happen...
// For the meantime, just use a system account
if ( !$caUser->attachedOn( WikiMap::getCurrentWikiId() ) && $user->getId() !== 0 ) {
return User::newSystemUser( 'Global rename script', [ 'steal' => true ] );
}
if ( $user->getId() == 0 ) {
// No local user, lets "auto-create" one
if ( CentralAuthServices::getUtilityService()->autoCreateUser( $user )->isGood() ) {
// So the internal cache is reloaded
return User::newFromName( $user->getName() );
}
// Auto-creation didn't work, fallback on the system account.
return User::newSystemUser( 'Global rename script', [ 'steal' => true ] );
}
// Account is attached and exists, just use it :)
return $user;
}
protected function done() {
$this->renameuserStatus->done( WikiMap::getCurrentWikiId() );
$caNew = CentralAuthUser::getInstanceByName( $this->params['to'] );
$caNew->quickInvalidateCache();
}
/**
* @param string $status
*/
protected function updateStatus( $status ) {
$this->renameuserStatus->updateStatus( WikiMap::getCurrentWikiId(), $status );
}
/**
* @param bool $status See Job::addTeardownCallback
*/
protected function scheduleNextWiki( $status ) {
if ( $status === false ) {
// This will lock the user out of their account until a sysadmin intervenes.
$this->updateStatus( 'failed' );
// Bail out just in case the error would affect all renames and continuing would
// just put all wikis of the user in failure state. Running the rename for this
// wiki again (e.g. with fixStuckGlobalRename.php) will resume the job chain.
return;
}
$job = new static( $this->getTitle(), $this->getParams() );
$nextWiki = null;
$statuses = $this->renameuserStatus->getStatuses( IDBAccessObject::READ_LATEST );
foreach ( $statuses as $wiki => $status ) {
if ( $status === 'queued' && $wiki !== WikiMap::getCurrentWikiId() ) {
$nextWiki = $wiki;
break;
}
}
if ( $nextWiki ) {
MediaWikiServices::getInstance()->getJobQueueGroupFactory()->makeJobQueueGroup( $nextWiki )->push( $job );
}
}
}

File Metadata

Mime Type
text/x-php
Expires
Aug 19 2026, 17:52 (5 w, 5 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ce/b5/7726abd1eca8891aae4b0b40f574
Default Alt Text
LocalRenameJob.php (6 KB)

Event Timeline