1<?php 2/** 3 * Reset the user_token for all users on the wiki. Useful if you believe 4 * that your user table was acidentally leaked to an external source. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 * http://www.gnu.org/copyleft/gpl.html 20 * 21 * @file 22 * @ingroup Maintenance 23 * @author Daniel Friesen <mediawiki@danielfriesen.name> 24 * @author Chris Steipp <csteipp@wikimedia.org> 25 */ 26 27require_once __DIR__ . '/Maintenance.php'; 28 29use MediaWiki\MediaWikiServices; 30 31/** 32 * Maintenance script to reset the user_token for all users on the wiki. 33 * 34 * @ingroup Maintenance 35 * @deprecated since 1.27, use $wgAuthenticationTokenVersion instead. 36 */ 37class ResetUserTokens extends Maintenance { 38 public function __construct() { 39 parent::__construct(); 40 $this->addDescription( 41 "Reset the user_token of all users on the wiki. Note that this may log some of them out.\n" 42 . "Deprecated, use \$wgAuthenticationTokenVersion instead." 43 ); 44 $this->addOption( 'nowarn', "Hides the 5 seconds warning", false, false ); 45 $this->addOption( 46 'nulls', 47 'Only reset tokens that are currently null (string of \x00\'s)', 48 false, 49 false 50 ); 51 $this->setBatchSize( 1000 ); 52 } 53 54 public function execute() { 55 $nullsOnly = $this->getOption( 'nulls' ); 56 57 if ( !$this->getOption( 'nowarn' ) ) { 58 if ( $nullsOnly ) { 59 $this->output( "The script is about to reset the user_token " 60 . "for USERS WITH NULL TOKENS in the database.\n" ); 61 } else { 62 $this->output( "The script is about to reset the user_token for ALL USERS in the database.\n" ); 63 $this->output( "This may log some of them out and is not necessary unless you believe your\n" ); 64 $this->output( "user table has been compromised.\n" ); 65 } 66 $this->output( "\n" ); 67 $this->output( "Abort with control-c in the next five seconds " 68 . "(skip this countdown with --nowarn) ... " ); 69 $this->countDown( 5 ); 70 } 71 72 // We list user by user_id from one of the replica DBs 73 $dbr = $this->getDB( DB_REPLICA ); 74 75 $where = []; 76 if ( $nullsOnly ) { 77 // Have to build this by hand, because \ is escaped in helper functions 78 $where = [ 'user_token = \'' . str_repeat( '\0', 32 ) . '\'' ]; 79 } 80 81 $maxid = $dbr->selectField( 'user', 'MAX(user_id)', [], __METHOD__ ); 82 83 $min = 0; 84 $max = $this->getBatchSize(); 85 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); 86 87 do { 88 $result = $dbr->select( 'user', 89 [ 'user_id' ], 90 array_merge( 91 $where, 92 [ 'user_id > ' . $dbr->addQuotes( $min ), 93 'user_id <= ' . $dbr->addQuotes( $max ) 94 ] 95 ), 96 __METHOD__ 97 ); 98 99 foreach ( $result as $user ) { 100 $this->updateUser( $user->user_id ); 101 } 102 103 $min = $max; 104 $max = $min + $this->getBatchSize(); 105 106 $lbFactory->waitForReplication(); 107 } while ( $min <= $maxid ); 108 } 109 110 private function updateUser( $userid ) { 111 $user = User::newFromId( $userid ); 112 $username = $user->getName(); 113 $this->output( 'Resetting user_token for "' . $username . '": ' ); 114 // Change value 115 $user->setToken(); 116 $user->saveSettings(); 117 $this->output( " OK\n" ); 118 } 119} 120 121$maintClass = ResetUserTokens::class; 122require_once RUN_MAINTENANCE_IF_MAIN; 123