Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
MigrateGuSalt
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 3
72
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getUpdateKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doDBUpdates
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @ingroup Maintenance
20 */
21
22namespace MediaWiki\Extension\CentralAuth\Maintenance;
23
24use MediaWiki\Maintenance\LoggedUpdateMaintenance;
25use Wikimedia\Rdbms\IExpression;
26use Wikimedia\Rdbms\LikeValue;
27
28$IP = getenv( 'MW_INSTALL_PATH' );
29if ( $IP === false ) {
30    $IP = __DIR__ . '/../../..';
31}
32require_once "$IP/maintenance/Maintenance.php";
33
34class MigrateGuSalt extends LoggedUpdateMaintenance {
35
36    public function __construct() {
37        parent::__construct();
38        $this->addDescription(
39            'Migrate all old type passwords which have their password stored in the gu_salt column'
40            . ' to have them being stored in the format :B:salt:password in the gu_password column'
41            . ' matching the format of the modern password system.'
42        );
43        $this->setBatchSize( 30 );
44        $this->requireExtension( 'CentralAuth' );
45    }
46
47    /**
48     * @inheritDoc
49     */
50    protected function getUpdateKey() {
51        return 'MigrateGuSalt';
52    }
53
54    /**
55     * @inheritDoc
56     */
57    public function doDBUpdates() {
58        $dbw = $this->getDB( DB_PRIMARY );
59
60        if ( !$dbw->tableExists( 'globaluser', __METHOD__ ) ) {
61            $this->output( "The globaluser table does not seem to exist.\n" );
62            return true;
63        }
64
65        if ( !$dbw->fieldExists( 'globaluser', 'gu_salt', __METHOD__ ) ) {
66            $this->output( "The gu_salt column does not seem to exist.\n" );
67            return true;
68        }
69
70        $typeCond = [
71            $dbw->expr( 'gu_salt', '!=', '' ),
72            $dbw->expr( 'gu_password', IExpression::NOT_LIKE, new LikeValue( ':', $dbw->anyString() ) ),
73        ];
74        $batchSize = $this->getBatchSize();
75
76        $count = 0;
77        $minUserId = 0;
78        while ( true ) {
79            $start = microtime( true );
80            $this->beginTransaction( $dbw, __METHOD__ );
81            $res = $dbw->newSelectQueryBuilder()
82                ->select( [ 'gu_id', 'gu_password', 'gu_salt' ] )
83                ->from( 'globaluser' )
84                ->where( $dbw->expr( 'gu_id', '>', $minUserId ) )
85                ->andWhere( $typeCond )
86                ->orderBy( 'gu_id' )
87                ->limit( $batchSize )
88                ->lockInShareMode()
89                ->caller( __METHOD__ )
90                ->fetchResultSet();
91
92            if ( $res->numRows() === 0 ) {
93                $this->commitTransaction( $dbw, __METHOD__ );
94                break;
95            }
96
97            foreach ( $res as $row ) {
98                $minUserId = $row->gu_id;
99                $count++;
100                $dbw->newUpdateQueryBuilder()
101                    ->update( 'globaluser' )
102                    ->set( [
103                        'gu_password' => ':B:' . $row->gu_salt . ':' . $row->gu_password,
104                        'gu_salt' => ''
105                    ] )
106                    ->where( [ 'gu_id' => $row->gu_id ] )
107                    ->caller( __METHOD__ )
108                    ->execute();
109            }
110
111            $this->commitTransaction( $dbw, __METHOD__ );
112
113            $this->output( "Last id processed: $minUserId; Actually updated: $count...\n" );
114            $delta = microtime( true ) - $start;
115            $this->output( sprintf(
116                "%4d password salts migrated in %6.2fms (%6.2fms each)\n",
117                $res->numRows(),
118                $delta * 1000.0,
119                ( $delta / $res->numRows() ) * 1000.0
120            ) );
121        }
122
123        $this->output( "$count users rows updated.\n" );
124        return true;
125    }
126}
127
128$maintClass = MigrateGuSalt::class;
129require_once RUN_MAINTENANCE_IF_MAIN;