addDescription( 'Wrap all passwords of a certain type in a new layered type' ); $this->addOption( 'type', 'Password type to wrap passwords in (must inherit LayeredParameterizedPassword)', true, true ); $this->addOption( 'verbose', 'Enables verbose output', false, false, 'v' ); $this->setBatchSize( 100 ); } public function execute() { $passwordFactory = MediaWikiServices::getInstance()->getPasswordFactory(); $typeInfo = $passwordFactory->getTypes(); $layeredType = $this->getOption( 'type' ); // Check that type exists and is a layered type if ( !isset( $typeInfo[$layeredType] ) ) { $this->fatalError( 'Undefined password type' ); } $passObj = $passwordFactory->newFromType( $layeredType ); if ( !$passObj instanceof LayeredParameterizedPassword ) { $this->fatalError( 'Layered parameterized password type must be used.' ); } // Extract the first layer type $typeConfig = $typeInfo[$layeredType]; $firstType = $typeConfig['types'][0]; // Get a list of password types that are applicable $dbw = $this->getDB( DB_MASTER ); $typeCond = 'user_password' . $dbw->buildLike( ":$firstType:", $dbw->anyString() ); $minUserId = 0; $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); do { $this->beginTransaction( $dbw, __METHOD__ ); $res = $dbw->select( 'user', [ 'user_id', 'user_name', 'user_password' ], [ 'user_id > ' . $dbw->addQuotes( $minUserId ), $typeCond ], __METHOD__, [ 'ORDER BY' => 'user_id', 'LIMIT' => $this->getBatchSize(), 'LOCK IN SHARE MODE', ] ); /** @var User[] $updateUsers */ $updateUsers = []; foreach ( $res as $row ) { if ( $this->hasOption( 'verbose' ) ) { $this->output( "Updating password for user {$row->user_name} ({$row->user_id}).\n" ); } $user = User::newFromId( $row->user_id ); /** @var ParameterizedPassword $password */ $password = $passwordFactory->newFromCiphertext( $row->user_password ); /** @var LayeredParameterizedPassword $layeredPassword */ $layeredPassword = $passwordFactory->newFromType( $layeredType ); $layeredPassword->partialCrypt( $password ); $updateUsers[] = $user; $dbw->update( 'user', [ 'user_password' => $layeredPassword->toString() ], [ 'user_id' => $row->user_id ], __METHOD__ ); $minUserId = $row->user_id; } $this->commitTransaction( $dbw, __METHOD__ ); $lbFactory->waitForReplication(); // Clear memcached so old passwords are wiped out foreach ( $updateUsers as $user ) { $user->clearSharedCache( 'refresh' ); } } while ( $res->numRows() ); } } $maintClass = WrapOldPasswords::class; require_once RUN_MAINTENANCE_IF_MAIN;