Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.87% covered (warning)
73.87%
82 / 111
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResetAuthenticationThrottle
73.87% covered (warning)
73.87%
82 / 111
50.00% covered (danger)
50.00%
3 / 6
41.98
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 execute
96.88% covered (success)
96.88%
31 / 32
0.00% covered (danger)
0.00%
0 / 1
15
 clearLoginThrottle
54.17% covered (warning)
54.17%
13 / 24
0.00% covered (danger)
0.00%
0 / 1
7.41
 clearSignupThrottle
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
 clearTempAccountCreationThrottle
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 clearTempAccountNameAcquisitionThrottle
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Reset login/signup throttling for a specified user and/or IP.
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Maintenance
8 */
9
10use MediaWiki\Auth\Throttler;
11use MediaWiki\Logger\LoggerFactory;
12use MediaWiki\MainConfigNames;
13use MediaWiki\Maintenance\Maintenance;
14use Wikimedia\IPUtils;
15
16// @codeCoverageIgnoreStart
17require_once __DIR__ . '/Maintenance.php';
18// @codeCoverageIgnoreEnd
19
20/**
21 * Reset login/signup throttling for a specified user and/or IP.
22 *
23 * @ingroup Maintenance
24 * @since 1.32
25 */
26class ResetAuthenticationThrottle extends Maintenance {
27
28    public function __construct() {
29        parent::__construct();
30        $this->addDescription( 'Reset login/signup throttling for a specified user and/or IP. '
31            . "\n\n"
32            . 'When resetting signup or temp account, provide the IP. When resetting login (or both), provide '
33            . 'both username (as entered in login screen) and IP. An easy way to obtain them is '
34            . "the 'throttler' log channel." );
35        $this->addOption( 'login', 'Reset login throttle' );
36        $this->addOption( 'signup', 'Reset account creation throttle' );
37        $this->addOption( 'tempaccount', 'Reset temp account creation throttle' );
38        $this->addOption( 'tempaccountnameacquisition', 'Reset temp account name acquisition throttle' );
39        $this->addOption( 'user', 'Username to reset (when using --login)', false, true );
40        $this->addOption( 'ip', 'IP to reset', false, true );
41    }
42
43    public function execute() {
44        $forLogin = (bool)$this->getOption( 'login' );
45        $forSignup = (bool)$this->getOption( 'signup' );
46        $forTempAccount = (bool)$this->getOption( 'tempaccount' );
47        $forTempAccountNameAcquisition = (bool)$this->getOption( 'tempaccountnameacquisition' );
48        $username = $this->getOption( 'user' );
49        $ip = $this->getOption( 'ip' );
50
51        if ( !$forLogin && !$forSignup && !$forTempAccount && !$forTempAccountNameAcquisition ) {
52            $this->fatalError(
53                'At least one of --login, --signup, --tempaccount, or --tempaccountnameacquisition is required!'
54            );
55        } elseif ( $ip === null ) {
56            $this->fatalError( '--ip is required!' );
57        } elseif ( !IPUtils::isValid( $ip ) ) {
58            $this->fatalError( "Not a valid IP: $ip" );
59        }
60
61        if ( $forLogin ) {
62            $this->clearLoginThrottle( $username, $ip );
63        }
64        if ( $forSignup ) {
65            $this->clearSignupThrottle( $ip );
66        }
67        if ( $forTempAccount ) {
68            $this->clearTempAccountCreationThrottle( $ip );
69        }
70        if ( $forTempAccountNameAcquisition ) {
71            $this->clearTempAccountNameAcquisitionThrottle( $ip );
72        }
73
74        LoggerFactory::getInstance( 'throttler' )->info( 'Manually cleared {type} throttle', [
75            'type' => implode( ' and ', array_filter( [
76                $forLogin ? 'login' : null,
77                $forSignup ? 'signup' : null,
78                $forTempAccount ? 'tempaccount' : null,
79                $forTempAccountNameAcquisition ? 'tempaccountnameacquisition' : null,
80            ] ) ),
81            'username' => $username,
82            'ipKey' => $ip,
83        ] );
84    }
85
86    /**
87     * @param string|null $rawUsername
88     * @param string|null $ip
89     */
90    protected function clearLoginThrottle( $rawUsername, $ip ) {
91        $this->output( 'Clearing login throttle...' );
92
93        $passwordAttemptThrottle = $this->getConfig()->get( MainConfigNames::PasswordAttemptThrottle );
94        if ( !$passwordAttemptThrottle ) {
95            $this->output( "none set\n" );
96            return;
97        }
98
99        $objectCacheFactory = $this->getServiceContainer()->getInstance()->getObjectCacheFactory();
100
101        $throttler = new Throttler( $passwordAttemptThrottle, [
102            'type' => 'password',
103            'cache' => $objectCacheFactory->getLocalClusterInstance(),
104        ] );
105        if ( $rawUsername !== null ) {
106            $usernames = $this->getServiceContainer()->getAuthManager()
107                ->normalizeUsername( $rawUsername );
108            if ( !$usernames ) {
109                $this->fatalError( "Not a valid username: $rawUsername" );
110            }
111        } else {
112            $usernames = [ null ];
113        }
114        foreach ( $usernames as $username ) {
115            $throttler->clear( $username, $ip );
116        }
117
118        $botPasswordThrottler = new Throttler( $passwordAttemptThrottle, [
119            'type' => 'botpassword',
120            'cache' => $objectCacheFactory->getLocalClusterInstance(),
121        ] );
122        $botPasswordThrottler->clear( $username, $ip );
123
124        $this->output( "done\n" );
125    }
126
127    /**
128     * @param string $ip
129     */
130    protected function clearSignupThrottle( $ip ) {
131        $this->output( 'Clearing signup throttle...' );
132
133        $accountCreationThrottle = $this->getConfig()->get( MainConfigNames::AccountCreationThrottle );
134        if ( !is_array( $accountCreationThrottle ) ) {
135            $accountCreationThrottle = [ [
136                'count' => $accountCreationThrottle,
137                'seconds' => 86400,
138            ] ];
139        }
140        if ( !$accountCreationThrottle ) {
141            $this->output( "none set\n" );
142            return;
143        }
144        $throttler = new Throttler( $accountCreationThrottle, [
145            'type' => 'acctcreate',
146            'cache' => $this->getServiceContainer()->getObjectCacheFactory()
147                ->getLocalClusterInstance(),
148        ] );
149
150        $throttler->clear( null, $ip );
151
152        $this->output( "done\n" );
153    }
154
155    protected function clearTempAccountCreationThrottle( string $ip ): void {
156        $this->output( 'Clearing temp account creation throttle...' );
157
158        $tempAccountCreationThrottle = $this->getConfig()->get( MainConfigNames::TempAccountCreationThrottle );
159        if ( !$tempAccountCreationThrottle ) {
160            $this->output( "none set\n" );
161            return;
162        }
163        $throttler = new Throttler( $tempAccountCreationThrottle, [
164            'type' => 'tempacctcreate',
165            'cache' => $this->getServiceContainer()->getObjectCacheFactory()
166                ->getLocalClusterInstance(),
167        ] );
168
169        $throttler->clear( null, $ip );
170
171        $this->output( "done\n" );
172    }
173
174    protected function clearTempAccountNameAcquisitionThrottle( string $ip ): void {
175        $this->output( 'Clearing temp account name acquisition throttle...' );
176
177        $tempAccountNameAcquisitionThrottle = $this->getConfig()->get(
178            MainConfigNames::TempAccountNameAcquisitionThrottle
179        );
180        if ( !$tempAccountNameAcquisitionThrottle ) {
181            $this->output( "none set\n" );
182            return;
183        }
184        $throttler = new Throttler( $tempAccountNameAcquisitionThrottle, [
185            'type' => 'tempacctnameacquisition',
186            'cache' => $this->getServiceContainer()->getObjectCacheFactory()
187                ->getLocalClusterInstance(),
188        ] );
189
190        $throttler->clear( null, $ip );
191
192        $this->output( "done\n" );
193    }
194
195}
196
197// @codeCoverageIgnoreStart
198$maintClass = ResetAuthenticationThrottle::class;
199require_once RUN_MAINTENANCE_IF_MAIN;
200// @codeCoverageIgnoreEnd