Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 94
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResetAuthenticationThrottle
0.00% covered (danger)
0.00%
0 / 91
0.00% covered (danger)
0.00%
0 / 5
930
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
380
 clearLoginThrottle
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
30
 clearSignupThrottle
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 clearTempAccountCreationThrottle
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Reset login/signup throttling for a specified user and/or IP.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Maintenance
22 */
23
24use MediaWiki\Auth\Throttler;
25use MediaWiki\Logger\LoggerFactory;
26use MediaWiki\MainConfigNames;
27use Wikimedia\IPUtils;
28
29require_once __DIR__ . '/Maintenance.php';
30
31/**
32 * Reset login/signup throttling for a specified user and/or IP.
33 *
34 * @ingroup Maintenance
35 * @since 1.32
36 */
37class ResetAuthenticationThrottle extends Maintenance {
38
39    public function __construct() {
40        parent::__construct();
41        $this->addDescription( 'Reset login/signup throttling for a specified user and/or IP. '
42            . "\n\n"
43            . 'When resetting signup or temp account, provide the IP. When resetting login (or both), provide '
44            . 'both username (as entered in login screen) and IP. An easy way to obtain them is '
45            . "the 'throttler' log channel." );
46        $this->addOption( 'login', 'Reset login throttle' );
47        $this->addOption( 'signup', 'Reset account creation throttle' );
48        $this->addOption( 'tempaccount', 'Reset temp account creation throttle' );
49        $this->addOption( 'user', 'Username to reset', false, true );
50        $this->addOption( 'ip', 'IP to reset', false, true );
51    }
52
53    public function execute() {
54        $forLogin = (bool)$this->getOption( 'login' );
55        $forSignup = (bool)$this->getOption( 'signup' );
56        $forTempAccount = (bool)$this->getOption( 'tempaccount' );
57        $username = $this->getOption( 'user' );
58        $ip = $this->getOption( 'ip' );
59
60        if ( !$forLogin && !$forSignup && !$forTempAccount ) {
61            $this->fatalError( 'At least one of --login, --signup or --tempaccount is required!' );
62        } elseif ( $forLogin && ( $ip === null || $username === null ) ) {
63            $this->fatalError( '--user and --ip are both required when using --login!' );
64        } elseif ( $forSignup && $ip === null ) {
65            $this->fatalError( '--ip is required when using --signup!' );
66        } elseif ( $forTempAccount && $ip === null ) {
67            $this->fatalError( '--ip is required when using --tempaccount!' );
68        } elseif ( $ip !== null && !IPUtils::isValid( $ip ) ) {
69            $this->fatalError( "Not a valid IP: $ip" );
70        }
71
72        if ( $forLogin ) {
73            $this->clearLoginThrottle( $username, $ip );
74        }
75        if ( $forSignup ) {
76            $this->clearSignupThrottle( $ip );
77        }
78        if ( $forTempAccount ) {
79            $this->clearTempAccountCreationThrottle( $ip );
80        }
81
82        LoggerFactory::getInstance( 'throttler' )->info( 'Manually cleared {type} throttle', [
83            'type' => implode( ' and ', array_filter( [
84                $forLogin ? 'login' : null,
85                $forSignup ? 'signup' : null,
86                $forTempAccount ? 'tempaccount' : null,
87            ] ) ),
88            'username' => $username,
89            'ipKey' => $ip,
90        ] );
91    }
92
93    /**
94     * @param string|null $rawUsername
95     * @param string|null $ip
96     */
97    protected function clearLoginThrottle( $rawUsername, $ip ) {
98        $this->output( 'Clearing login throttle...' );
99
100        $passwordAttemptThrottle = $this->getConfig()->get( MainConfigNames::PasswordAttemptThrottle );
101        if ( !$passwordAttemptThrottle ) {
102            $this->output( "none set\n" );
103            return;
104        }
105
106        $throttler = new Throttler( $passwordAttemptThrottle, [
107            'type' => 'password',
108            'cache' => ObjectCache::getLocalClusterInstance(),
109        ] );
110        if ( $rawUsername !== null ) {
111            $usernames = $this->getServiceContainer()->getAuthManager()
112                ->normalizeUsername( $rawUsername );
113            if ( !$usernames ) {
114                $this->fatalError( "Not a valid username: $rawUsername" );
115            }
116        } else {
117            $usernames = [ null ];
118        }
119        foreach ( $usernames as $username ) {
120            $throttler->clear( $username, $ip );
121        }
122
123        $botPasswordThrottler = new Throttler( $passwordAttemptThrottle, [
124            'type' => 'botpassword',
125            'cache' => ObjectCache::getLocalClusterInstance(),
126        ] );
127        // @phan-suppress-next-line PhanPossiblyUndeclaredVariable T240141
128        $botPasswordThrottler->clear( $username, $ip );
129
130        $this->output( "done\n" );
131    }
132
133    /**
134     * @param string $ip
135     */
136    protected function clearSignupThrottle( $ip ) {
137        $this->output( 'Clearing signup throttle...' );
138
139        $accountCreationThrottle = $this->getConfig()->get( MainConfigNames::AccountCreationThrottle );
140        if ( !is_array( $accountCreationThrottle ) ) {
141            $accountCreationThrottle = [ [
142                'count' => $accountCreationThrottle,
143                'seconds' => 86400,
144            ] ];
145        }
146        if ( !$accountCreationThrottle ) {
147            $this->output( "none set\n" );
148            return;
149        }
150        $throttler = new Throttler( $accountCreationThrottle, [
151            'type' => 'acctcreate',
152            'cache' => ObjectCache::getLocalClusterInstance(),
153        ] );
154
155        $throttler->clear( null, $ip );
156
157        $this->output( "done\n" );
158    }
159
160    protected function clearTempAccountCreationThrottle( string $ip ): void {
161        $this->output( 'Clearing temp account creation throttle...' );
162
163        $tempAccountCreationThrottle = $this->getConfig()->get( MainConfigNames::TempAccountCreationThrottle );
164        if ( !$tempAccountCreationThrottle ) {
165            $this->output( "none set\n" );
166            return;
167        }
168        $throttler = new Throttler( $tempAccountCreationThrottle, [
169            'type' => 'tempacctcreate',
170            'cache' => ObjectCache::getLocalClusterInstance(),
171        ] );
172
173        $throttler->clear( null, $ip );
174
175        $this->output( "done\n" );
176    }
177
178}
179
180$maintClass = ResetAuthenticationThrottle::class;
181require_once RUN_MAINTENANCE_IF_MAIN;