Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PopulateUserHandles
0.00% covered (danger)
0.00%
0 / 49
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 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 doDBUpdates
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
42
 getUpdateKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 *
5 * @file
6 * @ingroup Maintenance
7 */
8
9declare( strict_types = 1 );
10
11namespace MediaWiki\Extension\OATHAuth\Maintenance;
12
13use MediaWiki\Extension\OATHAuth\Module\WebAuthn;
14use MediaWiki\Extension\OATHAuth\OATHAuthServices;
15use MediaWiki\Json\FormatJson;
16use MediaWiki\Maintenance\LoggedUpdateMaintenance;
17use MediaWiki\Utils\BatchRowIterator;
18
19// @codeCoverageIgnoreStart
20$IP = getenv( 'MW_INSTALL_PATH' );
21if ( $IP === false ) {
22    $IP = __DIR__ . '/../../..';
23}
24
25require_once "$IP/maintenance/Maintenance.php";
26// @codeCoverageIgnoreEnd
27
28/**
29 * Populates the oathauth_user_handles table.
30 *
31 * Usage: php PopulateUserHandles.php
32 */
33class PopulateUserHandles extends LoggedUpdateMaintenance {
34
35    public function __construct() {
36        parent::__construct();
37        $this->requireExtension( 'OATHAuth' );
38        $this->addDescription( 'Populates the oathauth_user_handles table' );
39        $this->setBatchSize( 500 );
40    }
41
42    /** @inheritDoc */
43    protected function doDBUpdates() {
44        $startTime = time();
45        $updatedUsers = 0;
46        $totalUsers = 0;
47
48        $services = $this->getServiceContainer();
49
50        $moduleRegistry = OATHAuthServices::getInstance()->getModuleRegistry();
51        $webauthnModuleId = $moduleRegistry->getModuleId( WebAuthn::MODULE_ID );
52
53        $dbw = $services
54            ->getDBLoadBalancerFactory()
55            ->getPrimaryDatabase( 'virtual-oathauth' );
56
57        $sqb = $dbw->newSelectQueryBuilder()
58            ->select( [ 'oad_id', 'oad_user', 'oad_data' ] )
59            ->from( 'oathauth_devices' )
60            ->where( [ 'oad_type' => $webauthnModuleId ] )
61            ->caller( __METHOD__ );
62
63        $batches = new BatchRowIterator( $dbw, $sqb, 'oad_user', $this->getBatchSize() );
64
65        $prevUser = null;
66        foreach ( $batches as $rows ) {
67            $this->beginTransactionRound( __METHOD__ );
68
69            foreach ( $rows as $row ) {
70                // We may get multiple rows for the same user. If we already processed this user,
71                // skip this row.
72                if ( $row->oad_user === $prevUser ) {
73                    continue;
74                }
75
76                $keyData = FormatJson::decode( $row->oad_data, true );
77                if ( !isset( $keyData['userHandle'] ) ) {
78                    $this->error( "Key Id {$row->oad_id} doesn't have a userHandle. This shouldn't happen." );
79                    continue;
80                }
81
82                $dbw->newInsertQueryBuilder()
83                    ->insertInto( 'oathauth_user_handles' )
84                    ->ignore()
85                    ->row( [
86                        'oah_user' => $row->oad_user,
87                        // The userHandle value in $keyData is already base64-encoded, we don't need
88                        // to decode and re-encode it
89                        'oah_handle' => $keyData['userHandle']
90                    ] )
91                    ->caller( __METHOD__ )
92                    ->execute();
93
94                $prevUser = $row->oad_user;
95
96                $updatedUsers += $dbw->affectedRows();
97                $totalUsers++;
98
99                if ( $totalUsers % 50 === 0 ) {
100                    $this->output( "{$totalUsers}\n" );
101                }
102            }
103
104            $this->commitTransactionRound( __METHOD__ );
105        }
106
107        $totalTimeInSeconds = time() - $startTime;
108        $this->output( "Done. Processed {$totalUsers} users and inserted {$updatedUsers} rows " .
109            "in {$totalTimeInSeconds} seconds.\n" );
110        return true;
111    }
112
113    /** @return string */
114    protected function getUpdateKey() {
115        return __CLASS__;
116    }
117}
118
119// @codeCoverageIgnoreStart
120$maintClass = PopulateUserHandles::class;
121require_once RUN_MAINTENANCE_IF_MAIN;
122// @codeCoverageIgnoreEnd