Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
PurgeExpiredTemporaryRecoveryCodes
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 2
20
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
12
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\Key\RecoveryCodeKeys;
14use MediaWiki\Extension\OATHAuth\Module\RecoveryCodes;
15use MediaWiki\Extension\OATHAuth\OATHAuthServices;
16use MediaWiki\Json\FormatJson;
17use MediaWiki\Maintenance\Maintenance;
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
28class PurgeExpiredTemporaryRecoveryCodes extends Maintenance {
29
30    public function __construct() {
31        parent::__construct();
32        $this->addDescription( 'Purge expired temporary recovery codes' );
33    }
34
35    public function execute() {
36        $services = $this->getServiceContainer();
37        $moduleRegistry = OATHAuthServices::getInstance( $services )->getModuleRegistry();
38        $recoveryModuleId = $moduleRegistry->getModuleId( RecoveryCodes::MODULE_NAME );
39
40        $dbw = $services
41            ->getDBLoadBalancerFactory()
42            ->getPrimaryDatabase( 'virtual-oathauth' );
43        $res = $dbw->newSelectQueryBuilder()
44            ->select( [ 'oad_id', 'oad_data', 'oad_type' ] )
45            ->from( 'oathauth_devices' )
46            ->where( [ 'oad_type' => $recoveryModuleId ] )
47            ->caller( __METHOD__ )
48            ->fetchResultSet();
49
50        $totalRows = 0;
51        $updatedCount = 0;
52
53        foreach ( $res as $row ) {
54            $totalRows++;
55
56            $data = FormatJson::decode( $row->oad_data, true );
57
58            $numberOfKeys = count( $data['recoverycodekeys'] );
59
60            // Creating a new key object will remove expired codes.
61            // So if the count is the same, there are no expired codes to remove.
62            $key = RecoveryCodeKeys::newFromArray( $data );
63            if ( count( $key->getRecoveryCodes() ) === $numberOfKeys ) {
64                continue;
65            }
66
67            // Because expired codes have already been removed, we can just re-serialize and update in the DB
68            $dbw->newUpdateQueryBuilder()
69                ->update( 'oathauth_devices' )
70                ->set( [ 'oad_data' => FormatJson::encode( $key->jsonSerialize() ) ] )
71                ->where( [ 'oad_id' => $row->oad_id ] )
72                ->caller( __METHOD__ )
73                ->execute();
74
75            $updatedCount++;
76        }
77
78        $this->output( "Done. Updated {$updatedCount} of {$totalRows} rows.\n" );
79    }
80
81}
82
83// @codeCoverageIgnoreStart
84$maintClass = PurgeExpiredTemporaryRecoveryCodes::class;
85require_once RUN_MAINTENANCE_IF_MAIN;
86// @codeCoverageIgnoreEnd